subprocess 1.1.0 → 1.5.4

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 03feecd6b5ca0a66f4c795071be9ed48d4801ac8735e69660ff7e87b05f1e47d
4
+ data.tar.gz: 0dad63e1568ca3a477471b98b912ee0528963d80d1ee8646813b2efa7429d429
5
+ SHA512:
6
+ metadata.gz: 0d5cc41f0a7d716cb87c6e70c25b4892905b12d2c55727e29af281bc8e66f46be68a6a76fc263ac1ed1a8fd709288b3101dcd0d2ed19aac36c94d9de133ea171
7
+ data.tar.gz: 25b247b27b30ce8dc4cf98670adb415f82bc4e4ce64ba5e410307ba194efd516c13af19ded736568a2e95ab88bacda16a46b626230be45f2a948db7e7876ad7e
data/README.md CHANGED
@@ -1,15 +1,8 @@
1
- Subprocess
2
- ==========
1
+ # Subprocess [![Build Status](https://travis-ci.org/stripe/subprocess.svg?branch=master)](https://travis-ci.org/stripe/subprocess) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/stripe/subprocess/Subprocess)
3
2
 
4
3
  ![Jacques Cousteau Submarine](http://i.imgur.com/lmej24F.jpg)
5
4
 
6
- A port of Python's excellent subprocess module to Ruby.
7
-
8
- Many thanks to [Bram Swenson][bram], the author of the old [subprocess][old]
9
- gem, for graciously letting us use the name.
10
-
11
- [bram]: https://github.com/bramswenson
12
- [old]: https://github.com/bramswenson/subprocess
5
+ A solid subprocess library for ruby, inspired by python's.
13
6
 
14
7
  Installation
15
8
  ------------
@@ -22,20 +15,10 @@ You can also build `subprocess` from source by running:
22
15
 
23
16
  $ gem build subprocess.gemspec
24
17
 
25
-
26
18
  Usage
27
19
  -----
28
20
 
29
- Most of the documentation for Python's [subprocess][python] module applies
30
- equally well to this gem as well. While there are a few places when our
31
- semantics differs from Python's, users of the Python module should largely feel
32
- at home using `subprocess`. We have attempted to [document][rubydoc] all of the
33
- differences, but if we have missed something, please file an issue.
34
-
35
- [python]: http://docs.python.org/library/subprocess.html
36
- [rubydoc]: http://rubydoc.info/github/stripe/subprocess
37
-
38
- A few examples:
21
+ Full documentation is on [RubyDoc][rubydoc]. A few examples:
39
22
 
40
23
  ```ruby
41
24
  require 'subprocess'
@@ -72,3 +55,21 @@ http://upload.wikimedia.org/wikipedia/commons/3/3e/Unshorn_alpaca_grazing.jpg
72
55
  EMAIL
73
56
  end
74
57
  ```
58
+
59
+ Most of the documentation for Python's [subprocess][python] module applies
60
+ equally well to this gem as well. While there are a few places when our
61
+ semantics differs from Python's, users of the Python module should largely feel
62
+ at home using `subprocess`. We have attempted to [document][rubydoc] all of the
63
+ differences, but if we have missed something, please file an issue.
64
+
65
+ [python]: http://docs.python.org/library/subprocess.html
66
+ [rubydoc]: http://rubydoc.info/github/stripe/subprocess/Subprocess
67
+
68
+ Acknowledgements
69
+ ----------------
70
+
71
+ Many thanks to [Bram Swenson][bram], the author of the old [subprocess][old]
72
+ gem, for graciously letting us use the name.
73
+
74
+ [bram]: https://github.com/bramswenson
75
+ [old]: https://github.com/bramswenson/subprocess
@@ -16,7 +16,13 @@ module Subprocess
16
16
 
17
17
  # An alias for `Process.new`. Mostly here to better emulate the Python API.
18
18
  #
19
+ # @param [Array<String>] cmd See {Process#initialize}
20
+ # @param [Hash] opts See {Process#initialize}
21
+ # @yield [process] See {Process#initialize}
22
+ # @yieldparam process [Process] See {Process#initialize}
19
23
  # @return [Process] A process with the given arguments
24
+ #
25
+ # @see Process#initialize
20
26
  def self.popen(cmd, opts={}, &blk)
21
27
  Process.new(cmd, opts, &blk)
22
28
  end
@@ -28,9 +34,14 @@ module Subprocess
28
34
  # fills up, as neither file descriptor will be read from. To avoid this, use
29
35
  # {Process#communicate} from a passed block.
30
36
  #
37
+ # @param [Array<String>] cmd See {Process#initialize}
38
+ # @param [Hash] opts See {Process#initialize}
39
+ # @yield [process] See {Process#initialize}
40
+ # @yieldparam process [Process] See {Process#initialize}
41
+ #
31
42
  # @return [::Process::Status] The exit status of the process
32
43
  #
33
- # @see {Process#initialize}
44
+ # @see Process#initialize
34
45
  def self.call(cmd, opts={}, &blk)
35
46
  Process.new(cmd, opts, &blk).wait
36
47
  end
@@ -58,11 +69,16 @@ module Subprocess
58
69
  # fills up, as neither file descriptor will be read from. To avoid this, use
59
70
  # {Process#communicate} from a passed block.
60
71
  #
72
+ # @param [Array<String>] cmd See {Process#initialize}
73
+ # @param [Hash] opts See {Process#initialize}
74
+ # @yield [process] See {Process#initialize}
75
+ # @yieldparam process [Process] See {Process#initialize}
76
+ #
61
77
  # @raise [NonZeroExit] if the process returned a non-zero exit status (i.e.,
62
78
  # was terminated with an error or was killed by a signal)
63
79
  # @return [::Process::Status] The exit status of the process
64
80
  #
65
- # @see {Process#initialize}
81
+ # @see Process#initialize
66
82
  def self.check_call(cmd, opts={}, &blk)
67
83
  status = Process.new(cmd, opts, &blk).wait
68
84
  raise NonZeroExit.new(cmd, status) unless status.success?
@@ -70,16 +86,21 @@ module Subprocess
70
86
  end
71
87
 
72
88
  # Like {Subprocess::check_call}, but return the contents of `stdout`, much
73
- # like `Kernel#system`.
89
+ # like {::Kernel#system}.
74
90
  #
75
91
  # @example Get the system load
76
92
  # system_load = Subprocess.check_output(['uptime']).split(' ').last(3)
77
93
  #
94
+ # @param [Array<String>] cmd See {Process#initialize}
95
+ # @param [Hash] opts See {Process#initialize}
96
+ # @yield [process] See {Process#initialize}
97
+ # @yieldparam process [Process] See {Process#initialize}
98
+ #
78
99
  # @raise [NonZeroExit] if the process returned a non-zero exit status (i.e.,
79
100
  # was terminated with an error or was killed by a signal)
80
101
  # @return [String] The contents of `stdout`
81
102
  #
82
- # @see {Process#initialize}
103
+ # @see Process#initialize
83
104
  def self.check_output(cmd, opts={}, &blk)
84
105
  opts[:stdout] = PIPE
85
106
  child = Process.new(cmd, opts, &blk)
@@ -107,17 +128,7 @@ module Subprocess
107
128
  # been according to the usual exit status convention
108
129
  sig_num = status.exitstatus - 128
109
130
 
110
- # sigh, why is ruby so silly
111
- if Signal.respond_to?(:signame)
112
- # ruby 2.0 way
113
- sig_name = Signal.signame(sig_num)
114
- elsif Signal.list.respond_to?(:key)
115
- # ruby 1.9 way
116
- sig_name = Signal.list.key(sig_num)
117
- else
118
- # ruby 1.8 way
119
- sig_name = Signal.list.index(sig_num)
120
- end
131
+ sig_name = Signal.signame(sig_num)
121
132
 
122
133
  if sig_name
123
134
  parts << "(maybe SIG#{sig_name})"
@@ -140,16 +151,17 @@ module Subprocess
140
151
 
141
152
  # Error class representing a process's abnormal exit.
142
153
  class NonZeroExit < StandardError
143
- # @!attribute [r] command
144
- # @note This is intended only for use in user-facing error messages. In
145
- # particular, no shell quoting of any sort is performed when
146
- # constructing this string, meaning that blindly running it in a shell
147
- # might have different semantics than the original command.
148
- # @return [String] The command and arguments for the process that exited
149
- # abnormally.
150
- # @!attribute [r] status
151
- # @return [::Process::Status] The Ruby status object returned by `waitpid`
152
- attr_reader :command, :status
154
+ # @note This is intended only for use in user-facing error messages. In
155
+ # particular, no shell quoting of any sort is performed when
156
+ # constructing this string, meaning that blindly running it in a shell
157
+ # might have different semantics than the original command.
158
+ #
159
+ # @return [String] The command and arguments for the process that exited
160
+ # abnormally.
161
+ attr_reader :command
162
+
163
+ # @return [::Process::Status] The Ruby status object returned by `waitpid`
164
+ attr_reader :status
153
165
 
154
166
  # Return an instance of {NonZeroExit}.
155
167
  #
@@ -171,42 +183,63 @@ module Subprocess
171
183
  end
172
184
  end
173
185
 
186
+ # Error class representing a timeout during a call to `communicate`
187
+ class CommunicateTimeout < StandardError
188
+ # @return [String] Content read from stdout before the timeout
189
+ attr_reader :stdout
190
+
191
+ # @return [String] Content read from stderr before the timeout
192
+ attr_reader :stderr
193
+
194
+ # @param [Array<String>] cmd
195
+ # @param [String] stdout
196
+ # @param [String] stderr
197
+ def initialize(cmd, stdout, stderr)
198
+ @stdout = stdout
199
+ @stderr = stderr
200
+
201
+ super("Timeout communicating with `#{cmd.join(' ')}`")
202
+ end
203
+ end
204
+
174
205
  # A child process. The preferred way of spawning a subprocess is through the
175
206
  # functions on {Subprocess} (especially {Subprocess::check_call} and
176
207
  # {Subprocess::check_output}).
177
208
  class Process
178
- # @!attribute [r] stdin
179
- # @return [IO] The `IO` that is connected to this process's `stdin`.
180
- # @!attribute [r] stdout
181
- # @return [IO] The `IO` that is connected to this process's `stdout`.
182
- # @!attribute [r] stderr
183
- # @return [IO] The `IO` that is connected to this process's `stderr`.
184
- attr_reader :stdin, :stdout, :stderr
185
-
186
- # @!attribute [r] command
187
- # @return [Array<String>] The command this process was invoked with.
188
- # @!attribute [r] pid
189
- # @return [Fixnum] The process ID of the spawned process.
190
- # @!attribute [r] status
191
- # @return [::Process::Status] The exit status code of the process. Only
192
- # set after the process has exited.
193
- attr_reader :command, :pid, :status
209
+ # @return [IO] The `IO` that is connected to this process's `stdin`.
210
+ attr_reader :stdin
211
+
212
+ # @return [IO] The `IO` that is connected to this process's `stdout`.
213
+ attr_reader :stdout
214
+
215
+ # @return [IO] The `IO` that is connected to this process's `stderr`.
216
+ attr_reader :stderr
217
+
218
+ # @return [Array<String>] The command this process was invoked with.
219
+ attr_reader :command
220
+
221
+ # @return [Integer] The process ID of the spawned process.
222
+ attr_reader :pid
223
+
224
+ # @return [::Process::Status] The exit status code of the process. Only
225
+ # set after the process has exited.
226
+ attr_reader :status
194
227
 
195
228
  # Create a new process.
196
229
  #
197
230
  # @param [Array<String>] cmd The command to run and its arguments (in the
198
231
  # style of an `argv` array). Unlike Python's subprocess module, `cmd`
199
- # cannnot be a String.
232
+ # cannot be a String.
200
233
  #
201
- # @option opts [IO, Fixnum, String, Subprocess::PIPE, nil] :stdin The `IO`,
234
+ # @option opts [IO, Integer, String, Subprocess::PIPE, nil] :stdin The `IO`,
202
235
  # file descriptor number, or file name to use for the process's standard
203
236
  # input. If the magic value {Subprocess::PIPE} is passed, a new pipe will
204
237
  # be opened.
205
- # @option opts [IO, Fixnum, String, Subprocess::PIPE, nil] :stdout The `IO`,
238
+ # @option opts [IO, Integer, String, Subprocess::PIPE, nil] :stdout The `IO`,
206
239
  # file descriptor number, or file name to use for the process's standard
207
240
  # output. If the magic value {Subprocess::PIPE} is passed, a pipe will be
208
241
  # opened and attached to the process.
209
- # @option opts [IO, Fixnum, String, Subprocess::PIPE, Subprocess::STDOUT,
242
+ # @option opts [IO, Integer, String, Subprocess::PIPE, Subprocess::STDOUT,
210
243
  # nil] :stderr The `IO`, file descriptor number, or file name to use for
211
244
  # the process's standard error. If the special value {Subprocess::PIPE} is
212
245
  # passed, a pipe will be opened and attached to the process. If the
@@ -217,16 +250,15 @@ module Subprocess
217
250
  # child process.
218
251
  # @option opts [Hash<String, String>] :env The environment to use in the
219
252
  # child process.
220
- # @option opts [Array<Fixnum>] :retain_fds An array of file descriptor
253
+ # @option opts [Array<Integer>] :retain_fds An array of file descriptor
221
254
  # numbers that should not be closed before executing the child process.
222
255
  # Note that, unlike Python (which has :close_fds defaulting to false), all
223
256
  # file descriptors not specified here will be closed.
257
+ # @option opts [Hash] :exec_opts A hash that will be merged into the options
258
+ # hash of the call to {::Kernel#exec}.
224
259
  #
225
260
  # @option opts [Proc] :preexec_fn A function that will be called in the
226
- # child process immediately before executing `cmd`. Note: we don't
227
- # actually close file descriptors, but instead set them to auto-close on
228
- # `exec` (using `FD_CLOEXEC`), so your application will probably continue
229
- # to behave as expected.
261
+ # child process immediately before executing `cmd`.
230
262
  #
231
263
  # @yield [process] Yields the just-spawned {Process} to the optional block.
232
264
  # This occurs after all of {Process}'s error handling has been completed,
@@ -234,7 +266,8 @@ module Subprocess
234
266
  # in conjunction with {Subprocess::check_call}.
235
267
  # @yieldparam process [Process] The process that was just spawned.
236
268
  def initialize(cmd, opts={}, &blk)
237
- raise ArgumentError, "cmd must be an Array" unless Array === cmd
269
+ raise ArgumentError, "cmd must be an Array of strings" unless Array === cmd
270
+ raise ArgumentError, "cmd cannot be empty" if cmd.empty?
238
271
 
239
272
  @command = cmd
240
273
 
@@ -253,69 +286,6 @@ module Subprocess
253
286
 
254
287
  @pid = fork do
255
288
  begin
256
- require 'fcntl'
257
-
258
- FileUtils.cd(opts[:cwd]) if opts[:cwd]
259
-
260
- # The only way to mark an fd as CLOEXEC in ruby is to create an IO
261
- # object wrapping it. In 1.8, however, there's no way to create that
262
- # IO without it believing it owns the underlying fd, s.t. it will
263
- # close the fd if the IO is GC'd before the exec. Since we don't want
264
- # that, we stash a list of these IO objects to prevent them from
265
- # getting GC'd, since we are about to exec, which will clean
266
- # everything up anyways.
267
- fds = []
268
-
269
- # We have a whole ton of file descriptors that we don't want leaking
270
- # into the child. Set them all to close when we exec away.
271
- #
272
- # Ruby 1.9+ note: exec has a :close_others argument (and 2.0 closes
273
- # FDs by default). When we stop supporting Ruby 1.8, all of this can
274
- # go away.
275
- if File.directory?("/dev/fd")
276
- # On many modern UNIX-y systems, we can perform an optimization by
277
- # looking through /dev/fd, which is a sparse listing of all the
278
- # descriptors we have open. This allows us to avoid an expensive
279
- # linear scan.
280
- Dir.foreach("/dev/fd") do |file|
281
- fd = file.to_i
282
- if file.start_with?('.') || fd < 3 || retained_fds.include?(fd)
283
- next
284
- end
285
- begin
286
- fds << mark_fd_cloexec(fd)
287
- rescue Errno::EBADF
288
- # The fd might have been closed by now; that's peaceful.
289
- end
290
- end
291
- else
292
- # This is the big hammer. There's not really a good way of doing
293
- # this comprehensively across all platforms without just trying them
294
- # all. We only go up to the soft limit here. If you've been messing
295
- # with the soft limit, we might miss a few. Also, on OSX (perhaps
296
- # BSDs in general?), where the soft limit means something completely
297
- # different.
298
- special = [@child_stdin, @child_stdout, @child_stderr].compact
299
- special = Hash[special.map { |f| [f.fileno, f] }]
300
- 3.upto(::Process.getrlimit(::Process::RLIMIT_NOFILE).first) do |fd|
301
- next if retained_fds.include?(fd)
302
- begin
303
- # I don't know why we need to do this, but OSX started freaking
304
- # out when trying to dup2 below if FD_CLOEXEC had been set on a
305
- # fresh IO instance referring to the same underlying file
306
- # descriptor as what we were trying to dup2 from.
307
- if special[fd]
308
- special[fd].fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
309
- else
310
- fds << mark_fd_cloexec(fd)
311
- end
312
- rescue Errno::EBADF # Ignore FDs that don't exist
313
- end
314
- end
315
- end
316
-
317
- # dup2 the correct descriptors into place. Note that this clears the
318
- # FD_CLOEXEC flag on the new file descriptors (but not the old ones).
319
289
  ::STDIN.reopen(@child_stdin) if @child_stdin
320
290
  ::STDOUT.reopen(@child_stdout) if @child_stdout
321
291
  if opts[:stderr] == STDOUT
@@ -327,25 +297,44 @@ module Subprocess
327
297
  # Set up a new environment if we're requested to do so.
328
298
  if opts[:env]
329
299
  ENV.clear
330
- ENV.update(opts[:env])
300
+ begin
301
+ ENV.update(opts[:env])
302
+ rescue TypeError => e
303
+ raise ArgumentError, "`env` option must be a hash where all keys and values are strings (#{e})"
304
+ end
331
305
  end
332
306
 
333
307
  # Call the user back, maybe?
334
- opts[:preexec_fn].call if opts[:preexec_fn]
335
-
336
- # Ruby 1.8's exec is really stupid--there's no way to specify that
337
- # you want to exec a single thing *without* performing shell
338
- # expansion. So this is the next best thing.
339
- args = cmd
340
- if cmd.length == 1
341
- args = ["'" + cmd[0].gsub("'", "\\'") + "'"]
308
+ if opts[:preexec_fn]
309
+ if opts[:cwd]
310
+ Dir.chdir(opts[:cwd], &opts[:preexec_fn])
311
+ else
312
+ opts[:preexec_fn].call
313
+ end
342
314
  end
315
+
316
+ options = {close_others: true}.merge(opts.fetch(:exec_opts, {}))
343
317
  if opts[:retain_fds]
344
- redirects = {}
345
- retained_fds.each { |fd| redirects[fd] = fd }
346
- args << redirects
318
+ retained_fds.each { |fd| options[fd] = fd }
319
+ end
320
+ if opts[:cwd]
321
+ # We use the chdir option to `exec` since wrapping the
322
+ # `exec` in a Dir.chdir block caused these sporadic errors on macOS:
323
+ # Too many open files - getcwd (Errno::EMFILE)
324
+ options[:chdir] = opts[:cwd]
325
+ end
326
+
327
+ begin
328
+ # Ruby's Kernel#exec will call an exec(3) variant if called with two
329
+ # or more arguments, but when called with just a single argument will
330
+ # spawn a subshell with that argument as the command. Since we always
331
+ # want to call exec(3), we use the third exec form, which passes a
332
+ # [cmdname, argv0] array as its first argument and never invokes a
333
+ # subshell.
334
+ exec([cmd[0], cmd[0]], *cmd[1..-1], options)
335
+ rescue TypeError => e
336
+ raise ArgumentError, "cmd must be an Array of strings (#{e})"
347
337
  end
348
- exec(*args)
349
338
 
350
339
  rescue Exception => e
351
340
  # Dump all errors up to the parent through the control socket
@@ -360,12 +349,13 @@ module Subprocess
360
349
  # Meanwhile, in the parent process...
361
350
 
362
351
  # First, let's close some things we shouldn't have access to
363
- [@child_stdin, @child_stdout, @child_stderr, control_w].each do |fd|
364
- fd.close unless fd.nil?
365
- end
352
+ @child_stdin.close if our_fd?(opts[:stdin])
353
+ @child_stdout.close if our_fd?(opts[:stdout])
354
+ @child_stderr.close if our_fd?(opts[:stderr])
355
+ control_w.close
366
356
 
367
357
  # Any errors during the spawn process? We'll get past this point when the
368
- # child execs and the OS closes control_w because of the FD_CLOEXEC
358
+ # child execs and the OS closes control_w
369
359
  begin
370
360
  e = Marshal.load(control_r)
371
361
  e = "Unknown Failure" unless e.is_a?(Exception) || e.is_a?(String)
@@ -407,7 +397,7 @@ module Subprocess
407
397
  # condition (`EOFError` or `EPIPE`).
408
398
  def drain_fd(fd, buf=nil)
409
399
  loop do
410
- tmp = fd.read_nonblock(4096)
400
+ tmp = fd.read_nonblock(4096).force_encoding(fd.external_encoding)
411
401
  buf << tmp unless buf.nil?
412
402
  end
413
403
  rescue EOFError, Errno::EPIPE
@@ -418,40 +408,48 @@ module Subprocess
418
408
  false
419
409
  end
420
410
 
421
- # Write the (optional) input to the process's `stdin`. Also, read (and
422
- # buffer in memory) the contents of `stdout` and `stderr`. Do this all using
423
- # `IO::select`, so we don't deadlock due to full pipe buffers.
411
+ # Write the (optional) input to the process's `stdin` and read the contents of
412
+ # `stdout` and `stderr`. If a block is provided, stdout and stderr are yielded as they
413
+ # are read. Otherwise they are buffered in memory and returned when the process
414
+ # exits. Do this all using `IO::select`, so we don't deadlock due to full pipe
415
+ # buffers.
424
416
  #
425
417
  # This is only really useful if you set some of `:stdin`, `:stdout`, and
426
418
  # `:stderr` to {Subprocess::PIPE}.
427
419
  #
428
420
  # @param [String] input A string to feed to the child's standard input.
429
- # @return [Array<String>] An array of two elements: the data read from the
421
+ # @param [Numeric] timeout_s Raise {Subprocess::CommunicateTimeout} if communicate
422
+ # does not finish after timeout_s seconds.
423
+ # @yieldparam [String] stdout Data read from stdout since the last yield
424
+ # @yieldparam [String] stderr Data read from stderr since the last yield
425
+ # @return [Array(String, String), nil] An array of two elements: the data read from the
430
426
  # child's standard output and standard error, respectively.
431
- def communicate(input=nil)
427
+ # Returns nil if a block is provided.
428
+ def communicate(input=nil, timeout_s=nil)
432
429
  raise ArgumentError if !input.nil? && @stdin.nil?
433
430
 
434
431
  stdout, stderr = "", ""
432
+
435
433
  input = input.dup unless input.nil?
436
434
 
437
435
  @stdin.close if (input.nil? || input.empty?) && !@stdin.nil?
438
436
 
439
- self_read, self_write = IO.pipe
440
- self.class.catching_sigchld(pid, self_write) do
441
- wait_r = [@stdout, @stderr, self_read].compact
437
+ timeout_at = Time.now + timeout_s if timeout_s
438
+
439
+ self.class.catching_sigchld(pid) do |global_read, self_read|
440
+ wait_r = [@stdout, @stderr, self_read, global_read].compact
442
441
  wait_w = [input && @stdin].compact
443
- loop do
444
- ready_r, ready_w = select(wait_r, wait_w)
445
-
446
- # If the child exits, we still have to be sure to read any data left
447
- # in the pipes. So we poll the child, drain all the pipes, and *then*
448
- # check @status.
449
- #
450
- # It's very important that we do not call poll between draining the
451
- # pipes and checking @status. If we did, we open a race condition
452
- # where the child writes to stdout and exits in that brief window,
453
- # causing us to lose that data.
454
- poll
442
+ done = false
443
+ while !done
444
+ # If the process has exited, we want to drain any remaining output before returning
445
+ if poll
446
+ ready_r = wait_r - [self_read, global_read]
447
+ ready_w = []
448
+ done = true
449
+ else
450
+ ready_r, ready_w = select_until(wait_r, wait_w, [], timeout_at)
451
+ raise CommunicateTimeout.new(@command, stdout, stderr) if ready_r.nil?
452
+ end
455
453
 
456
454
  if ready_r.include?(@stdout)
457
455
  if drain_fd(@stdout, stdout)
@@ -465,6 +463,13 @@ module Subprocess
465
463
  end
466
464
  end
467
465
 
466
+ if ready_r.include?(global_read)
467
+ if drain_fd(global_read)
468
+ raise "Unexpected internal error -- someone closed the global self-pipe!"
469
+ end
470
+ self.class.wakeup_sigchld
471
+ end
472
+
468
473
  if ready_r.include?(self_read)
469
474
  if drain_fd(self_read)
470
475
  raise "Unexpected internal error -- someone closed our self-pipe!"
@@ -472,10 +477,24 @@ module Subprocess
472
477
  end
473
478
 
474
479
  if ready_w.include?(@stdin)
480
+ written = 0
475
481
  begin
476
482
  written = @stdin.write_nonblock(input)
477
483
  rescue EOFError # Maybe I shouldn't catch this...
478
484
  rescue Errno::EINTR
485
+ rescue IO::WaitWritable
486
+ # On OS X, a pipe can raise EAGAIN even after select indicates
487
+ # that it is writable. Once the process consumes from the pipe,
488
+ # the next write should succeed and we should make forward progress.
489
+ # Until then, treat this as not writing any bytes and continue looping.
490
+ # For details see: https://github.com/stripe/subprocess/pull/22
491
+ nil
492
+ rescue Errno::EPIPE
493
+ # The other side of the pipe closed before we could
494
+ # write all of our input. This can happen if the
495
+ # process exits prematurely.
496
+ @stdin.close
497
+ wait_w.delete(@stdin)
479
498
  end
480
499
  input[0...written] = ''
481
500
  if input.empty?
@@ -484,29 +503,41 @@ module Subprocess
484
503
  end
485
504
  end
486
505
 
487
- break if @status
488
-
489
- # If there's nothing left to wait for, we're done!
490
- break if wait_r.length == 0 && wait_w.length == 0
506
+ if block_given? && !(stderr.empty? && stdout.empty?)
507
+ yield stdout, stderr
508
+ stdout, stderr = "", ""
509
+ end
491
510
  end
492
511
  end
493
512
 
494
513
  wait
495
514
 
496
- [stdout, stderr]
515
+ if block_given?
516
+ nil
517
+ else
518
+ [stdout, stderr]
519
+ end
497
520
  end
498
521
 
499
522
  # Does exactly what it says on the box.
500
523
  #
501
- # @param [String, Symbol, Fixnum] signal The signal to send to the child
524
+ # @param [String, Symbol, Integer] signal The signal to send to the child
502
525
  # process. Accepts all the same arguments as Ruby's built-in
503
526
  # {::Process::kill}, for instance a string like "INT" or "SIGINT", or a
504
527
  # signal number like 2.
528
+ #
529
+ # @return [Integer] See {::Process.kill}
530
+ #
531
+ # @see ::Process.kill
505
532
  def send_signal(signal)
506
533
  ::Process.kill(signal, pid)
507
534
  end
508
535
 
509
536
  # Sends `SIGTERM` to the process.
537
+ #
538
+ # @return [Integer] See {send_signal}
539
+ #
540
+ # @see send_signal
510
541
  def terminate
511
542
  send_signal("TERM")
512
543
  end
@@ -517,16 +548,17 @@ module Subprocess
517
548
  # "mine" is only non-nil in the case of a pipe (in fact, we just return a
518
549
  # list of length one, since ruby will unpack nils from missing list items).
519
550
  #
520
- # If you pass either an IO or an Integer (i.e., a raw file descriptor), a
521
- # private copy of it will be made using `#dup`.
551
+ # @param [IO, Integer, String, nil] fd
552
+ # @param [String] mode
553
+ # @return [Array<IO>]
522
554
  def parse_fd(fd, mode)
523
555
  fds = case fd
524
556
  when PIPE
525
557
  IO.pipe
526
558
  when IO
527
- [fd.dup]
559
+ [fd]
528
560
  when Integer
529
- [IO.new(fd, mode).dup]
561
+ [IO.new(fd, mode)]
530
562
  when String
531
563
  [File.open(fd, mode)]
532
564
  when nil
@@ -538,42 +570,106 @@ module Subprocess
538
570
  mode == 'r' ? fds : fds.reverse
539
571
  end
540
572
 
541
- def mark_fd_cloexec(fd)
542
- io = IO.new(fd)
543
- io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
544
- io
545
- rescue ArgumentError => e
546
- # Ruby maintains a self-pipe for thread interrupts, but it handles closing
547
- # it on forks/execs
548
- raise unless e.message == "The given fd is not accessible because RubyVM reserves it"
573
+ # The pair to parse_fd, returns whether or not the file descriptor was
574
+ # opened by us (and therefore should be closed by us).
575
+ #
576
+ # @param [IO, Integer, String, nil] fd
577
+ # @return [Boolean]
578
+ def our_fd?(fd)
579
+ case fd
580
+ when PIPE, String
581
+ true
582
+ else
583
+ false
584
+ end
585
+ end
586
+
587
+ # Call IO.select timing out at Time `timeout_at`. If `timeout_at` is nil, never times out.
588
+ #
589
+ # @param [Array<IO>, nil] read_array
590
+ # @param [Array<IO>, nil] write_array
591
+ # @param [Array<IO>, nil] err_array
592
+ # @param [Integer, Float, nil] timeout_at
593
+ # @return [Array<Array<IO>>, nil]
594
+ def select_until(read_array, write_array, err_array, timeout_at)
595
+ if !timeout_at
596
+ return IO.select(read_array, write_array, err_array)
597
+ end
598
+
599
+ remaining = (timeout_at - Time.now)
600
+ return nil if remaining <= 0
601
+
602
+ IO.select(read_array, write_array, err_array, remaining)
549
603
  end
550
604
 
551
605
  @sigchld_mutex = Mutex.new
552
606
  @sigchld_fds = {}
553
607
  @sigchld_old_handler = nil
608
+ @sigchld_global_write = nil
609
+ @sigchld_global_read = nil
610
+ @sigchld_pipe_pid = nil
611
+
612
+ # @return [void]
613
+ def self.handle_sigchld
614
+ # We'd like to just notify everything in `@sigchld_fds`, but
615
+ # ruby signal handlers are not executed atomically with respect
616
+ # to other Ruby threads, so reading it is racy. We can't grab
617
+ # `@sigchld_mutex`, because signal execution blocks the main
618
+ # thread, and so we'd deadlock if the main thread currently
619
+ # holds it.
620
+ #
621
+ # Instead, we keep a long-lived notify self-pipe that we select
622
+ # on inside `communicate`, and we task `communicate` with
623
+ # grabbing the lock and fanning out the wakeups.
624
+ begin
625
+ @sigchld_global_write.write_nonblock("\x00")
626
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
627
+ nil # ignore
628
+ end
629
+ end
554
630
 
555
631
  # Wake up everyone. We can't tell who we should wake up without `wait`ing,
556
632
  # and we want to let the process itself do that. In practice, we're not
557
633
  # likely to have that many in-flight subprocesses, so this is probably not a
558
634
  # big deal.
559
- def self.handle_sigchld
560
- @sigchld_fds.values.each do |fd|
561
- begin
562
- fd.write_nonblock("\x00")
563
- rescue Errno::EWOULDBLOCK, Errno::EAGAIN
635
+ # @return [void]
636
+ def self.wakeup_sigchld
637
+ @sigchld_mutex.synchronize do
638
+ @sigchld_fds.values.each do |fd|
639
+ begin
640
+ fd.write_nonblock("\x00")
641
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
642
+ # If the pipe is full, the other end will be woken up
643
+ # regardless when it next reads, so it's fine to skip the
644
+ # write (the pipe is a wakeup channel, and doesn't contain
645
+ # meaningful data).
646
+ end
564
647
  end
565
648
  end
566
649
  end
567
650
 
651
+ # @param [Integer] pid
652
+ # @param [IO] fd
653
+ # @return [void]
568
654
  def self.register_pid(pid, fd)
569
655
  @sigchld_mutex.synchronize do
570
656
  @sigchld_fds[pid] = fd
571
657
  if @sigchld_fds.length == 1
658
+ if @sigchld_global_write.nil? || @sigchld_pipe_pid != ::Process.pid
659
+ # Check the PID so that if we fork we will re-open the
660
+ # pipe. It's important that a fork parent and child don't
661
+ # share this pipe, because if they do they risk stealing
662
+ # each others' wakeups.
663
+ @sigchld_pipe_pid = ::Process.pid
664
+ @sigchld_global_read, @sigchld_global_write = IO.pipe
665
+ end
572
666
  @sigchld_old_handler = Signal.trap('SIGCHLD') {handle_sigchld}
573
667
  end
574
668
  end
575
669
  end
576
670
 
671
+ # @param [Integer] pid
672
+ # @return [void]
577
673
  def self.unregister_pid(pid)
578
674
  @sigchld_mutex.synchronize do
579
675
  if @sigchld_fds.length == 1
@@ -583,11 +679,17 @@ module Subprocess
583
679
  end
584
680
  end
585
681
 
586
- def self.catching_sigchld(pid, fd)
587
- register_pid(pid, fd)
588
- yield
589
- ensure
590
- unregister_pid(pid)
682
+ # @param [Integer] pid
683
+ # @return [void]
684
+ def self.catching_sigchld(pid)
685
+ IO.pipe do |self_read, self_write|
686
+ begin
687
+ register_pid(pid, self_write)
688
+ yield @sigchld_global_read, self_read
689
+ ensure
690
+ unregister_pid(pid)
691
+ end
692
+ end
591
693
  end
592
694
  end
593
695
  end
@@ -1,3 +1,3 @@
1
1
  module Subprocess
2
- VERSION = '1.1.0'
2
+ VERSION = '1.5.4'
3
3
  end
@@ -0,0 +1,312 @@
1
+ # typed: strong
2
+
3
+ # THIS FILE IS AUTOGENERATED. DO NOT EDIT.
4
+ # To regenerate from YARD comments:
5
+ #
6
+ # bundle exec rake sord
7
+ #
8
+ # A Ruby clone of Python's subprocess module.
9
+ #
10
+ # @see http://docs.python.org/2/library/subprocess.html
11
+ module ::Subprocess
12
+ PIPE = T.let(-1, T.untyped)
13
+ STDOUT = T.let(-2, T.untyped)
14
+ VERSION = T.let('1.5.4', T.untyped)
15
+
16
+ # An alias for `Process.new`. Mostly here to better emulate the Python API.
17
+ #
18
+ # _@param_ `cmd` — See {Process#initialize}
19
+ #
20
+ # _@param_ `opts` — See {Process#initialize}
21
+ #
22
+ # _@return_ — A process with the given arguments
23
+ #
24
+ # _@see_ `Process#initialize`
25
+ sig { params(cmd: T::Array[String], opts: T::Hash[T.untyped, T.untyped], blk: T.nilable(T.proc.params(process: Process).void)).returns(Process) }
26
+ def self.popen(cmd, opts = {}, &blk); end
27
+
28
+ # Call and wait for the return of a given process.
29
+ #
30
+ # _@param_ `cmd` — See {Process#initialize}
31
+ #
32
+ # _@param_ `opts` — See {Process#initialize}
33
+ #
34
+ # _@return_ — The exit status of the process
35
+ #
36
+ # _@note_ — If you call this function with `:stdout => PIPE` or `:stderr => PIPE`,
37
+ # this function will block indefinitely as soon as the OS's pipe buffer
38
+ # fills up, as neither file descriptor will be read from. To avoid this, use
39
+ # {Process#communicate} from a passed block.
40
+ #
41
+ # _@see_ `Process#initialize`
42
+ sig { params(cmd: T::Array[String], opts: T::Hash[T.untyped, T.untyped], blk: T.nilable(T.proc.params(process: Process).void)).returns(::Process::Status) }
43
+ def self.call(cmd, opts = {}, &blk); end
44
+
45
+ # Like {Subprocess::call}, except raise a {NonZeroExit} if the process did not
46
+ # terminate successfully.
47
+ #
48
+ # _@param_ `cmd` — See {Process#initialize}
49
+ #
50
+ # _@param_ `opts` — See {Process#initialize}
51
+ #
52
+ # _@return_ — The exit status of the process
53
+ #
54
+ # Grep a file for a string
55
+ # ```ruby
56
+ # Subprocess.check_call(%W{grep -q llama ~/favorite_animals})
57
+ # ```
58
+ #
59
+ # Communicate with a child process
60
+ # ```ruby
61
+ # Subprocess.check_call(%W{sendmail -t}, :stdin => Subprocess::PIPE) do |p|
62
+ # p.communicate <<-EMAIL
63
+ # From: alpaca@example.com
64
+ # To: llama@example.com
65
+ # Subject: I am so fluffy.
66
+ #
67
+ # SO FLUFFY!
68
+ # http://upload.wikimedia.org/wikipedia/commons/3/3e/Unshorn_alpaca_grazing.jpg
69
+ # EMAIL
70
+ # end
71
+ # ```
72
+ #
73
+ # _@note_ — If you call this function with `:stdout => PIPE` or `:stderr => PIPE`,
74
+ # this function will block indefinitely as soon as the OS's pipe buffer
75
+ # fills up, as neither file descriptor will be read from. To avoid this, use
76
+ # {Process#communicate} from a passed block.
77
+ #
78
+ # _@see_ `Process#initialize`
79
+ sig { params(cmd: T::Array[String], opts: T::Hash[T.untyped, T.untyped], blk: T.nilable(T.proc.params(process: Process).void)).returns(::Process::Status) }
80
+ def self.check_call(cmd, opts = {}, &blk); end
81
+
82
+ # Like {Subprocess::check_call}, but return the contents of `stdout`, much
83
+ # like {::Kernel#system}.
84
+ #
85
+ # _@param_ `cmd` — See {Process#initialize}
86
+ #
87
+ # _@param_ `opts` — See {Process#initialize}
88
+ #
89
+ # _@return_ — The contents of `stdout`
90
+ #
91
+ # Get the system load
92
+ # ```ruby
93
+ # system_load = Subprocess.check_output(['uptime']).split(' ').last(3)
94
+ # ```
95
+ #
96
+ # _@see_ `Process#initialize`
97
+ sig { params(cmd: T::Array[String], opts: T::Hash[T.untyped, T.untyped], blk: T.nilable(T.proc.params(process: Process).void)).returns(String) }
98
+ def self.check_output(cmd, opts = {}, &blk); end
99
+
100
+ # Print a human readable interpretation of a process exit status.
101
+ #
102
+ # _@param_ `status` — The status returned by `waitpid2`.
103
+ #
104
+ # _@param_ `convert_high_exit` — Whether to convert exit statuses greater than 128 into the usual convention for exiting after trapping a signal. (e.g. many programs will exit with status 130 after receiving a SIGINT / signal 2.)
105
+ #
106
+ # _@return_ — Text interpretation
107
+ sig { params(status: ::Process::Status, convert_high_exit: T::Boolean).returns(String) }
108
+ def self.status_to_s(status, convert_high_exit = true); end
109
+
110
+ # Error class representing a process's abnormal exit.
111
+ class NonZeroExit < StandardError
112
+ # Return an instance of {NonZeroExit}.
113
+ #
114
+ # _@param_ `cmd` — The command that returned a non-zero status.
115
+ #
116
+ # _@param_ `status` — The status returned by `waitpid`.
117
+ sig { params(cmd: T::Array[String], status: ::Process::Status).void }
118
+ def initialize(cmd, status); end
119
+
120
+ # _@return_ — The command and arguments for the process that exited
121
+ # abnormally.
122
+ #
123
+ # _@note_ — This is intended only for use in user-facing error messages. In
124
+ # particular, no shell quoting of any sort is performed when
125
+ # constructing this string, meaning that blindly running it in a shell
126
+ # might have different semantics than the original command.
127
+ sig { returns(String) }
128
+ attr_reader :command
129
+
130
+ # _@return_ — The Ruby status object returned by `waitpid`
131
+ sig { returns(::Process::Status) }
132
+ attr_reader :status
133
+ end
134
+
135
+ # Error class representing a timeout during a call to `communicate`
136
+ class CommunicateTimeout < StandardError
137
+ # _@param_ `cmd`
138
+ #
139
+ # _@param_ `stdout`
140
+ #
141
+ # _@param_ `stderr`
142
+ sig { params(cmd: T::Array[String], stdout: String, stderr: String).void }
143
+ def initialize(cmd, stdout, stderr); end
144
+
145
+ # _@return_ — Content read from stdout before the timeout
146
+ sig { returns(String) }
147
+ attr_reader :stdout
148
+
149
+ # _@return_ — Content read from stderr before the timeout
150
+ sig { returns(String) }
151
+ attr_reader :stderr
152
+ end
153
+
154
+ # A child process. The preferred way of spawning a subprocess is through the
155
+ # functions on {Subprocess} (especially {Subprocess::check_call} and
156
+ # {Subprocess::check_output}).
157
+ class Process
158
+ # Create a new process.
159
+ #
160
+ # _@param_ `cmd` — The command to run and its arguments (in the style of an `argv` array). Unlike Python's subprocess module, `cmd` cannot be a String.
161
+ sig { params(cmd: T::Array[String], opts: T::Hash[T.untyped, T.untyped], blk: T.nilable(T.proc.params(process: Process).void)).void }
162
+ def initialize(cmd, opts = {}, &blk); end
163
+
164
+ # Poll the child, setting (and returning) its status. If the child has not
165
+ # terminated, return nil and exit immediately
166
+ #
167
+ # _@return_ — The exit status of the process
168
+ sig { returns(T.nilable(::Process::Status)) }
169
+ def poll; end
170
+
171
+ # Wait for the child to return, setting and returning the status of the
172
+ # child.
173
+ #
174
+ # _@return_ — The exit status of the process
175
+ sig { returns(::Process::Status) }
176
+ def wait; end
177
+
178
+ # Do nonblocking reads from `fd`, appending all data read into `buf`.
179
+ #
180
+ # _@param_ `fd` — The file to read from.
181
+ #
182
+ # _@param_ `buf` — A buffer to append the read data to.
183
+ #
184
+ # _@return_ — Whether `fd` was closed due to an exceptional
185
+ # condition (`EOFError` or `EPIPE`).
186
+ sig { params(fd: IO, buf: T.nilable(String)).returns(T::Boolean) }
187
+ def drain_fd(fd, buf = nil); end
188
+
189
+ # Write the (optional) input to the process's `stdin` and read the contents of
190
+ # `stdout` and `stderr`. If a block is provided, stdout and stderr are yielded as they
191
+ # are read. Otherwise they are buffered in memory and returned when the process
192
+ # exits. Do this all using `IO::select`, so we don't deadlock due to full pipe
193
+ # buffers.
194
+ #
195
+ # This is only really useful if you set some of `:stdin`, `:stdout`, and
196
+ # `:stderr` to {Subprocess::PIPE}.
197
+ #
198
+ # _@param_ `input` — A string to feed to the child's standard input.
199
+ #
200
+ # _@param_ `timeout_s` — Raise {Subprocess::CommunicateTimeout} if communicate does not finish after timeout_s seconds.
201
+ #
202
+ # _@return_ — An array of two elements: the data read from the
203
+ # child's standard output and standard error, respectively.
204
+ # Returns nil if a block is provided.
205
+ sig { params(input: T.nilable(String), timeout_s: T.nilable(Numeric)).returns([String, String]) }
206
+ def communicate(input = nil, timeout_s = nil); end
207
+
208
+ # Does exactly what it says on the box.
209
+ #
210
+ # _@param_ `signal` — The signal to send to the child process. Accepts all the same arguments as Ruby's built-in {::Process::kill}, for instance a string like "INT" or "SIGINT", or a signal number like 2.
211
+ #
212
+ # _@return_ — See {::Process.kill}
213
+ #
214
+ # _@see_ `::Process.kill`
215
+ sig { params(signal: T.any(String, Symbol, Integer)).returns(Integer) }
216
+ def send_signal(signal); end
217
+
218
+ # Sends `SIGTERM` to the process.
219
+ #
220
+ # _@return_ — See {send_signal}
221
+ #
222
+ # _@see_ `send_signal`
223
+ sig { returns(Integer) }
224
+ def terminate; end
225
+
226
+ # Return a pair of values (child, mine), which are how the given file
227
+ # descriptor should appear to the child and to this process, respectively.
228
+ # "mine" is only non-nil in the case of a pipe (in fact, we just return a
229
+ # list of length one, since ruby will unpack nils from missing list items).
230
+ #
231
+ # _@param_ `fd`
232
+ #
233
+ # _@param_ `mode`
234
+ sig { params(fd: T.nilable(T.any(IO, Integer, String)), mode: String).returns(T::Array[IO]) }
235
+ def parse_fd(fd, mode); end
236
+
237
+ # The pair to parse_fd, returns whether or not the file descriptor was
238
+ # opened by us (and therefore should be closed by us).
239
+ #
240
+ # _@param_ `fd`
241
+ sig { params(fd: T.nilable(T.any(IO, Integer, String))).returns(T::Boolean) }
242
+ def our_fd?(fd); end
243
+
244
+ # Call IO.select timing out at Time `timeout_at`. If `timeout_at` is nil, never times out.
245
+ #
246
+ # _@param_ `read_array`
247
+ #
248
+ # _@param_ `write_array`
249
+ #
250
+ # _@param_ `err_array`
251
+ #
252
+ # _@param_ `timeout_at`
253
+ sig do
254
+ params(
255
+ read_array: T.nilable(T::Array[IO]),
256
+ write_array: T.nilable(T::Array[IO]),
257
+ err_array: T.nilable(T::Array[IO]),
258
+ timeout_at: T.nilable(T.any(Integer, Float))
259
+ ).returns(T.nilable(T::Array[T::Array[IO]]))
260
+ end
261
+ def select_until(read_array, write_array, err_array, timeout_at); end
262
+
263
+ sig { void }
264
+ def self.handle_sigchld; end
265
+
266
+ # Wake up everyone. We can't tell who we should wake up without `wait`ing,
267
+ # and we want to let the process itself do that. In practice, we're not
268
+ # likely to have that many in-flight subprocesses, so this is probably not a
269
+ # big deal.
270
+ sig { void }
271
+ def self.wakeup_sigchld; end
272
+
273
+ # _@param_ `pid`
274
+ #
275
+ # _@param_ `fd`
276
+ sig { params(pid: Integer, fd: IO).void }
277
+ def self.register_pid(pid, fd); end
278
+
279
+ # _@param_ `pid`
280
+ sig { params(pid: Integer).void }
281
+ def self.unregister_pid(pid); end
282
+
283
+ # _@param_ `pid`
284
+ sig { params(pid: Integer).void }
285
+ def self.catching_sigchld(pid); end
286
+
287
+ # _@return_ — The `IO` that is connected to this process's `stdin`.
288
+ sig { returns(IO) }
289
+ attr_reader :stdin
290
+
291
+ # _@return_ — The `IO` that is connected to this process's `stdout`.
292
+ sig { returns(IO) }
293
+ attr_reader :stdout
294
+
295
+ # _@return_ — The `IO` that is connected to this process's `stderr`.
296
+ sig { returns(IO) }
297
+ attr_reader :stderr
298
+
299
+ # _@return_ — The command this process was invoked with.
300
+ sig { returns(T::Array[String]) }
301
+ attr_reader :command
302
+
303
+ # _@return_ — The process ID of the spawned process.
304
+ sig { returns(Integer) }
305
+ attr_reader :pid
306
+
307
+ # _@return_ — The exit status code of the process. Only
308
+ # set after the process has exited.
309
+ sig { returns(::Process::Status) }
310
+ attr_reader :status
311
+ end
312
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: subprocess
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.5.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Carl Jackson
@@ -10,57 +9,65 @@ authors:
10
9
  - Nelson Elhage
11
10
  - Andy Brody
12
11
  - Andreas Fuchs
13
- autorequire:
12
+ autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
- date: 2014-04-30 00:00:00.000000000 Z
15
+ date: 2021-01-13 00:00:00.000000000 Z
17
16
  dependencies:
18
17
  - !ruby/object:Gem::Dependency
19
18
  name: minitest
20
19
  requirement: !ruby/object:Gem::Requirement
21
- none: false
22
20
  requirements:
23
- - - ~>
21
+ - - "~>"
24
22
  - !ruby/object:Gem::Version
25
23
  version: '5.0'
26
24
  type: :development
27
25
  prerelease: false
28
26
  version_requirements: !ruby/object:Gem::Requirement
29
- none: false
30
27
  requirements:
31
- - - ~>
28
+ - - "~>"
32
29
  - !ruby/object:Gem::Version
33
30
  version: '5.0'
34
31
  - !ruby/object:Gem::Dependency
35
32
  name: rake
36
33
  requirement: !ruby/object:Gem::Requirement
37
- none: false
38
34
  requirements:
39
- - - ! '>='
35
+ - - ">="
40
36
  - !ruby/object:Gem::Version
41
37
  version: '0'
42
38
  type: :development
43
39
  prerelease: false
44
40
  version_requirements: !ruby/object:Gem::Requirement
45
- none: false
46
41
  requirements:
47
- - - ! '>='
42
+ - - ">="
48
43
  - !ruby/object:Gem::Version
49
44
  version: '0'
50
45
  - !ruby/object:Gem::Dependency
51
46
  name: pry
52
47
  requirement: !ruby/object:Gem::Requirement
53
- none: false
54
48
  requirements:
55
- - - ! '>='
49
+ - - ">="
56
50
  - !ruby/object:Gem::Version
57
51
  version: '0'
58
52
  type: :development
59
53
  prerelease: false
60
54
  version_requirements: !ruby/object:Gem::Requirement
61
- none: false
62
55
  requirements:
63
- - - ! '>='
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ - !ruby/object:Gem::Dependency
60
+ name: sord
61
+ requirement: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
64
71
  - !ruby/object:Gem::Version
65
72
  version: '0'
66
73
  description: Control and communicate with spawned processes
@@ -74,33 +81,31 @@ executables: []
74
81
  extensions: []
75
82
  extra_rdoc_files: []
76
83
  files:
77
- - lib/subprocess/version.rb
78
- - lib/subprocess.rb
79
84
  - README.md
85
+ - lib/subprocess.rb
86
+ - lib/subprocess/version.rb
87
+ - rbi/subprocess.rbi
80
88
  homepage: https://github.com/stripe/subprocess
81
89
  licenses:
82
90
  - MIT
83
- post_install_message:
91
+ metadata: {}
92
+ post_install_message:
84
93
  rdoc_options: []
85
94
  require_paths:
86
95
  - lib
87
96
  required_ruby_version: !ruby/object:Gem::Requirement
88
- none: false
89
97
  requirements:
90
- - - ! '>='
98
+ - - ">="
91
99
  - !ruby/object:Gem::Version
92
100
  version: '0'
93
101
  required_rubygems_version: !ruby/object:Gem::Requirement
94
- none: false
95
102
  requirements:
96
- - - ! '>='
103
+ - - ">="
97
104
  - !ruby/object:Gem::Version
98
105
  version: '0'
99
106
  requirements: []
100
- rubyforge_project:
101
- rubygems_version: 1.8.23
102
- signing_key:
103
- specification_version: 3
107
+ rubygems_version: 3.1.2
108
+ signing_key:
109
+ specification_version: 4
104
110
  summary: A port of Python's subprocess module to Ruby
105
111
  test_files: []
106
- has_rdoc: