open4 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. data/lib/open4.rb +19 -2
  2. data/open4.gemspec +2 -1
  3. data/test/popen4ext_test.rb +82 -0
  4. metadata +21 -42
@@ -4,7 +4,7 @@ require 'timeout'
4
4
  require 'thread'
5
5
 
6
6
  module Open4
7
- VERSION = '1.2.0'
7
+ VERSION = '1.3.0'
8
8
  def self.version() VERSION end
9
9
 
10
10
  class Error < ::StandardError; end
@@ -38,7 +38,17 @@ module Open4
38
38
  module_function :popen4
39
39
  module_function :open4
40
40
 
41
- def self.do_popen(b = nil, exception_propagation_at = nil, &cmd)
41
+ def popen4ext(closefds=false, *cmd, &b)
42
+ Open4.do_popen(b, :init, closefds) do |ps_read, ps_write|
43
+ ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
44
+ ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
45
+ exec(*cmd)
46
+ raise 'forty-two' # Is this really needed?
47
+ end
48
+ end
49
+ module_function :popen4ext
50
+
51
+ def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
42
52
  pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
43
53
 
44
54
  verbose = $VERBOSE
@@ -46,6 +56,13 @@ module Open4
46
56
  $VERBOSE = nil
47
57
 
48
58
  cid = fork {
59
+ if closefds
60
+ exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
61
+ ObjectSpace.each_object(IO){|io|
62
+ io.close if (not io.closed?) and (not exlist.include? io.fileno)
63
+ }
64
+ end
65
+
49
66
  pw.last.close
50
67
  STDIN.reopen pw.first
51
68
  pw.first.close
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification::new do |spec|
5
5
  spec.name = "open4"
6
- spec.version = "1.2.0"
6
+ spec.version = "1.3.0"
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.summary = "open4"
9
9
  spec.description = "description: open4 kicks the ass"
@@ -29,6 +29,7 @@ Gem::Specification::new do |spec|
29
29
  "test",
30
30
  "test/pfork4_test.rb",
31
31
  "test/popen4_test.rb",
32
+ "test/popen4ext_test.rb",
32
33
  "test/support",
33
34
  "test/support/test_case.rb",
34
35
  "white_box",
@@ -0,0 +1,82 @@
1
+ require 'test_case'
2
+
3
+ module Open4
4
+
5
+ class POpen4Test < TestCase
6
+ UNKNOWN_CMD = 'asdfadsfjlkkk'
7
+ UNKNOWN_CMD_ERRORS = [Errno::ENOENT, Errno::EINVAL]
8
+
9
+ def test_unknown_command_propagates_exception
10
+ err = assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD }
11
+ assert_match /#{UNKNOWN_CMD}/, err.to_s if on_mri?
12
+ end
13
+
14
+ def test_exception_propagation_avoids_zombie_child_process
15
+ assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD }
16
+ assert_empty Process.waitall
17
+ end
18
+
19
+ def test_exit_failure
20
+ code = 43
21
+ cid, _ = popen4ext true, %{ruby -e "exit #{43}"}
22
+ assert_equal code, wait_status(cid)
23
+ end
24
+
25
+ def test_exit_success
26
+ cid, _ = popen4ext true, %{ruby -e "exit"}
27
+ assert_equal 0, wait_status(cid)
28
+ end
29
+
30
+ def test_passes_child_pid_to_block
31
+ cmd = %{ruby -e "STDOUT.print Process.pid"}
32
+ cid_in_block = nil
33
+ cid_in_fun = nil
34
+ status = popen4ext(true, cmd) do |cid, _, stdout, _|
35
+ cid_in_block = cid
36
+ cid_in_fun = stdout.read.to_i
37
+ end
38
+ assert_equal cid_in_fun, cid_in_block
39
+ end
40
+
41
+ def test_io_pipes_without_block
42
+ via_msg = 'foo'
43
+ err_msg = 'bar'
44
+ cmd = <<-END
45
+ ruby -e "
46
+ STDOUT.write STDIN.read
47
+ STDERR.write '#{err_msg}'
48
+ "
49
+ END
50
+ cid, stdin, stdout, stderr = popen4ext true, cmd
51
+ stdin.write via_msg
52
+ stdin.close
53
+ out_actual = stdout.read
54
+ err_actual = stderr.read
55
+ assert_equal via_msg, out_actual
56
+ assert_equal err_msg, err_actual
57
+ assert_equal 0, wait_status(cid)
58
+ end
59
+
60
+ def test_io_pipes_with_block
61
+ via_msg = 'foo'
62
+ err_msg = 'bar'
63
+ out_actual, err_actual = nil
64
+ cmd = <<-END
65
+ ruby -e "
66
+ STDOUT.write STDIN.read
67
+ STDERR.write '#{err_msg}'
68
+ "
69
+ END
70
+ status = popen4ext(true, cmd) do |_, stdin, stdout, stderr|
71
+ stdin.write via_msg
72
+ stdin.close
73
+ out_actual = stdout.read
74
+ err_actual = stderr.read
75
+ end
76
+ assert_equal via_msg, out_actual
77
+ assert_equal err_msg, err_actual
78
+ assert_equal 0, status.exitstatus
79
+ end
80
+ end
81
+
82
+ end
metadata CHANGED
@@ -1,32 +1,22 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: open4
3
- version: !ruby/object:Gem::Version
4
- hash: 31
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
5
  prerelease:
6
- segments:
7
- - 1
8
- - 2
9
- - 0
10
- version: 1.2.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Ara T. Howard
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-10-05 00:00:00 Z
12
+ date: 2011-11-19 00:00:00.000000000 Z
19
13
  dependencies: []
20
-
21
- description: "description: open4 kicks the ass"
14
+ description: ! 'description: open4 kicks the ass'
22
15
  email: ara.t.howard@gmail.com
23
16
  executables: []
24
-
25
17
  extensions: []
26
-
27
18
  extra_rdoc_files: []
28
-
29
- files:
19
+ files:
30
20
  - LICENSE
31
21
  - README
32
22
  - README.erb
@@ -44,42 +34,31 @@ files:
44
34
  - samples/timeout.rb
45
35
  - test/pfork4_test.rb
46
36
  - test/popen4_test.rb
37
+ - test/popen4ext_test.rb
47
38
  - test/support/test_case.rb
48
39
  - white_box/leak.rb
49
40
  homepage: https://github.com/ahoward/open4
50
41
  licenses: []
51
-
52
- metadata: {}
53
-
54
42
  post_install_message:
55
43
  rdoc_options: []
56
-
57
- require_paths:
44
+ require_paths:
58
45
  - lib
59
- required_ruby_version: !ruby/object:Gem::Requirement
46
+ required_ruby_version: !ruby/object:Gem::Requirement
60
47
  none: false
61
- requirements:
62
- - - ">="
63
- - !ruby/object:Gem::Version
64
- hash: 3
65
- segments:
66
- - 0
67
- version: "0"
68
- required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
53
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
- version: "0"
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
77
58
  requirements: []
78
-
79
59
  rubyforge_project: codeforpeople
80
- rubygems_version: 1.8.10
60
+ rubygems_version: 1.8.11
81
61
  signing_key:
82
- specification_version: 4
62
+ specification_version: 3
83
63
  summary: open4
84
64
  test_files: []
85
-