diff --git a/lib/open3.rb b/lib/open3.rb index 5ff1012..ef0cdcd 100644 --- a/lib/open3.rb +++ b/lib/open3.rb @@ -223,6 +223,7 @@ class << self # opts[:stdin_data] and opts[:binmode]. See Process.spawn. # # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # It can be given as a String or an IO. # # If opts[:binmode] is true, internal pipes are set to binary mode. # @@ -248,13 +249,13 @@ class << self # # But if the image is stored in a DB or generated by the gnuplot Open3.capture2 example, # # Open3.capture3 should be considered. # # - # image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true) + # image = File.open("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true) # thumbnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true) # if s.success? # STDOUT.binmode; print thumbnail # end # - def capture3(*cmd, stdin_data: '', binmode: false, **opts) + def capture3(*cmd, stdin_data: nil, binmode: false, **opts) popen3(*cmd, opts) {|i, o, e, t| if binmode i.binmode @@ -263,9 +264,15 @@ def capture3(*cmd, stdin_data: '', binmode: false, **opts) end out_reader = Thread.new { o.read } err_reader = Thread.new { e.read } - begin - i.write stdin_data - rescue Errno::EPIPE + if stdin_data + begin + if stdin_data.respond_to?(:read) + IO.copy_stream(stdin_data, i) + else + i.write stdin_data + end + rescue Errno::EPIPE + end end i.close [out_reader.value, err_reader.value, t.value] @@ -281,6 +288,7 @@ def capture3(*cmd, stdin_data: '', binmode: false, **opts) # opts[:stdin_data] and opts[:binmode]. See Process.spawn. # # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # It can be given as a String or an IO. # # If opts[:binmode] is true, internal pipes are set to binary mode. # @@ -311,7 +319,11 @@ def capture2(*cmd, stdin_data: nil, binmode: false, **opts) out_reader = Thread.new { o.read } if stdin_data begin - i.write stdin_data + if stdin_data.respond_to?(:read) + IO.copy_stream(stdin_data, i) + else + i.write stdin_data + end rescue Errno::EPIPE end end @@ -329,6 +341,7 @@ def capture2(*cmd, stdin_data: nil, binmode: false, **opts) # opts[:stdin_data] and opts[:binmode]. See Process.spawn. # # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # It can be given as a String or an IO. # # If opts[:binmode] is true, internal pipes are set to binary mode. # @@ -346,7 +359,11 @@ def capture2e(*cmd, stdin_data: nil, binmode: false, **opts) outerr_reader = Thread.new { oe.read } if stdin_data begin - i.write stdin_data + if stdin_data.respond_to?(:read) + IO.copy_stream(stdin_data, i) + else + i.write stdin_data + end rescue Errno::EPIPE end end diff --git a/test/test_open3.rb b/test/test_open3.rb index d52ab6a..0015c57 100644 --- a/test/test_open3.rb +++ b/test/test_open3.rb @@ -2,6 +2,7 @@ require 'test/unit' require 'open3' +require 'stringio' class TestOpen3 < Test::Unit::TestCase RUBY = EnvUtil.rubybin @@ -162,32 +163,70 @@ def test_capture3_flip assert(s.success?) end + def test_capture3_stdin_string + o, e, s = Open3.capture3(RUBY, '-e', 'print STDIN.read', :stdin_data=>"z"*(1024*1024)) + assert_equal("z"*(1024*1024), o) + assert_equal("", e) + assert(s.success?) + end + + def test_capture3_stdin_io + o, e, s = Open3.capture3(RUBY, '-e', 'print STDIN.read', :stdin_data=>StringIO.new("z"*(1024*1024))) + assert_equal("z"*(1024*1024), o) + assert_equal("", e) + assert(s.success?) + end + + def test_capture3_stdin_closed + o, e, s = Open3.capture3(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024)) + assert_equal("", o) + assert_equal("", e) + assert(s.success?) + end + def test_capture2 o, s = Open3.capture2(RUBY, '-e', 'i=STDIN.read; print i+"o"', :stdin_data=>"i") assert_equal("io", o) assert(s.success?) end + def test_capture2_stdin_string + o, s = Open3.capture2(RUBY, '-e', 'print STDIN.read', :stdin_data=>"z"*(1024*1024)) + assert_equal("z"*(1024*1024), o) + assert(s.success?) + end + + def test_capture2_stdin_io + o, s = Open3.capture2(RUBY, '-e', 'print STDIN.read', :stdin_data=>StringIO.new("z"*(1024*1024))) + assert_equal("z"*(1024*1024), o) + assert(s.success?) + end + + def test_capture2_stdin_closed + o, s = Open3.capture2(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024)) + assert_equal("", o) + assert(s.success?) + end + def test_capture2e oe, s = Open3.capture2e(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>"i") assert_equal("ioie", oe) assert(s.success?) end - def test_capture3_stdin_data - o, e, s = Open3.capture3(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024)) - assert_equal("", o) - assert_equal("", e) + def test_capture2e_stdin_string + oe, s = Open3.capture2e(RUBY, '-e', 'print STDIN.read', :stdin_data=>"z"*(1024*1024)) + assert_equal("z"*(1024*1024), oe) assert(s.success?) end - def test_capture2_stdin_data - o, s = Open3.capture2(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024)) - assert_equal("", o) + def test_capture2e_stdin_io + oe, s = Open3.capture2e(RUBY, '-e', 'print STDIN.read', :stdin_data=>StringIO.new("z"*(1024*1024))) + assert_equal("z"*(1024*1024), oe) assert(s.success?) end - def test_capture2e_stdin_data + def test_capture2e_stdin_closed oe, s = Open3.capture2e(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024)) assert_equal("", oe) assert(s.success?)