subprocess 1.1.0 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: