Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

POSIX::Spawn::Child cleanup and docs

  • Loading branch information...
commit 169d7750f776c736339c9fee7b2a19d875f54b58 1 parent 1e59698
@rtomayko authored
View
2  TODO
@@ -11,7 +11,7 @@
[x] POSIX::Spawn::Process.new should have same method signature as Process::spawn
[x] POSIX::Spawn::Process renamed to POSIX::Spawn::Child
[x] Better POSIX::Spawn#spawn comment docs
-[ ] POSIX::Spawn::Child usage examples in README
+[x] POSIX::Spawn::Child usage examples in README
[ ] popen* interfaces
View
4 lib/posix/spawn.rb
@@ -32,7 +32,7 @@ module POSIX
# unset in the child:
#
# # set FOO as BAR and unset BAZ.
- # pid = spawn({"FOO" => "BAR", "BAZ" => nil}, 'echo', 'hello world')
+ # spawn({"FOO" => "BAR", "BAZ" => nil}, 'echo', 'hello world')
#
# == Command
#
@@ -58,7 +58,7 @@ module POSIX
#
# The :chdir option specifies the current directory:
#
- # pid = spawn(command, :chdir => "/var/tmp")
+ # spawn(command, :chdir => "/var/tmp")
#
# The :in, :out, :err, a Fixnum, an IO object or an Array option specify
# fd redirection. For example, stderr can be merged into stdout as follows:
View
82 lib/posix/spawn/child.rb
@@ -1,74 +1,80 @@
module POSIX
module Spawn
# POSIX::Spawn::Child includes logic for executing child processes and
- # reading/writing from their standard input, output, and error streams.
+ # reading/writing from their standard input, output, and error streams. It's
+ # designed to take all input in a single string and provides all output
+ # (stderr and stdout) as single strings and is therefore not well-suited
+ # to streaming large quantities of data in and out of commands.
#
- # Create an run a process to completion:
+ # Create and run a process to completion:
#
- # >> process = POSIX::Spawn::Child.new(['git', '--help'])
+ # >> child = POSIX::Spawn::Child.new('git', '--help')
#
# Retrieve stdout or stderr output:
#
- # >> process.out
+ # >> child.out
# => "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]\n ..."
- # >> process.err
+ # >> child.err
# => ""
#
# Check process exit status information:
#
- # >> process.status
+ # >> child.status
# => #<Process::Status: pid=80718,exited(0)>
#
- # POSIX::Spawn::Child is designed to take all input in a single string and
- # provides all output as single strings. It is therefore not well suited
- # to streaming large quantities of data in and out of commands.
+ # To write data on the new process's stdin immediately after spawning:
+ #
+ # >> child = POSIX::Spawn::Child.new('bc', :input => '40 + 2')
+ # >> child.out
+ # "42\n"
#
- # Q: Why not use popen3 or hand-roll fork/exec code?
+ # Q: Why use POSIX::Spawn::Child instead of popen3, hand rolled fork/exec
+ # code, or Process::spawn?
#
# - It's more efficient than popen3 and provides meaningful process
# hierarchies because it performs a single fork/exec. (popen3 double forks
# to avoid needing to collect the exit status and also calls
# Process::detach which creates a Ruby Thread!!!!).
#
- # - It's more portable than hand rolled pipe, fork, exec code because
- # fork(2) and exec(2) aren't available on all platforms. In those cases,
- # POSIX::Spawn::Child falls back to using whatever janky substitutes the platform
- # provides.
+ # - It handles all max pipe buffer (PIPE_BUF) hang cases when reading and
+ # writing semi-large amounts of data. This is non-trivial to implement
+ # correctly and must be accounted for with popen3, spawn, or hand rolled
+ # fork/exec code.
#
- # - It handles all max pipe buffer hang cases, which is non trivial to
- # implement correctly and must be accounted for with either popen3 or
- # hand rolled fork/exec code.
+ # - It's more portable than hand rolled pipe, fork, exec code because
+ # fork(2) and exec aren't available on all platforms. In those cases,
+ # POSIX::Spawn::Child falls back to using whatever janky substitutes
+ # the platform provides.
class Child
include POSIX::Spawn
- # Create and execute a new process.
+ # Spawn a new process, write all input and read all output, and wait for
+ # the program to exit. Supports the standard spawn interface as described
+ # in the POSIX::Spawn module documentation:
#
- # argv - Array of [command, arg1, ...] strings to use as the new
- # process's argv. When argv is a String, the shell is used
- # to interpret the command.
- # env - The new process's environment variables. This is merged with
- # the current environment as if by ENV.merge(env).
- # options - Additional options:
- # :input => str to write str to the process's stdin.
- # :timeout => int number of seconds before we given up.
- # :max => total number of output bytes
- # A subset of Process::spawn options are also supported on all
- # platforms:
- # :chdir => str to start the process in different working dir.
+ # new([env], command, [argv1, ...], [options])
#
- # Returns a new Child instance that has already executed to completion.
- # The out, err, and status attributes are immediately available.
- def initialize(*argv)
- env, argv, options = extract_process_spawn_arguments(*argv)
- @argv = argv
- @env = env
-
+ # The following options are supported in addition to the standard
+ # POSIX::Spawn options:
+ #
+ # :input => str Write str to the new process's standard input.
+ # :timeout => int Maximum number of seconds to allow the process
+ # to execute before aborting with a TimeoutExceeded
+ # exception.
+ # :max => total Maximum number of bytes of output to allow the
+ # process to generate before aborting with a
+ # MaximumOutputExceeded exception.
+ #
+ # Returns a new Child instance whose underlying process has already
+ # executed to completion. The out, err, and status attributes are
+ # immediately available.
+ def initialize(*args)
+ @env, @argv, options = extract_process_spawn_arguments(*args)
@options = options.dup
@input = @options.delete(:input)
@timeout = @options.delete(:timeout)
@max = @options.delete(:max)
@options.delete(:chdir) if @options[:chdir].nil?
-
exec!
end
View
6 test/test_child.rb
@@ -74,13 +74,13 @@ def test_max_with_stubborn_child
def test_timeout
assert_raise TimeoutExceeded do
- Child.new('sleep 1', :timeout => 0.05)
+ Child.new('sleep', '1', :timeout => 0.05)
end
end
def test_timeout_with_child_hierarchy
assert_raise TimeoutExceeded do
- Child.new('/bin/sh', '-c', 'yes', :timeout => 0.05)
+ Child.new('/bin/sh', '-c', 'sleep 1', :timeout => 0.05)
end
end
@@ -93,7 +93,7 @@ def test_lots_of_input_and_lots_of_output_at_the_same_time
echo stuff on stderr 1>&2;
done
"
- p = Child.new('/bin/sh', '-c', command, :input => input)
+ p = Child.new(command, :input => input)
assert_equal input.size, p.out.size
assert_equal input.size, p.err.size
assert p.success?
Please sign in to comment.
Something went wrong with that request. Please try again.