Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add ensure_open_and_* methods to StringIO. #2246

Closed
wants to merge 5 commits into from

4 participants

@warrenseen

These methods are required to allow IO.copy_stream to work against a StringIO
instance.

Alternatively, it may be better to refactor IO.copy_stream to not use these
methods, as they aren't API-compatible with the MRI IO implementation?

@warrenseen warrenseen Add ensure_open_and_* methods to StringIO.
These methods are required to allow IO.copy_stream to work against a StringIO
instance.

Alternatively, it may be better to refactor IO.copy_stream to not use these
methods, as they aren't API-compatible with the MRI IO implementation?
460c240
@wilson
Collaborator

This looks totally sane to me, given the rest of the current implementation. I also agree with you that the 'internal' protocol here could be clearer.

lib/19/stringio.rb
@@ -306,6 +309,10 @@ def pid
nil
end
+ def pipe?
+ true
+ end
@dbussink Owner

Any reason this returns true and not false?

Good catch. I think this actually needs to be false here. The pipe flag is only used in the StreamCopier to avoid trying to call seek on a pipe - since StringIO appears to support seek calls happily, this should be ok to change. Will fix.

So looking at the MRI source, it turns out that IO.copy_stream raises an error when passed an offset when the source IO isn't backed by a file descriptor. Adjusting behaviour on this also.

@dbussink Owner

Ah, so it means that the pipe? method probably can go for StringIO right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@warrenseen

OK, I've just realised that the copy_stream spec doesn't fully pass on MRI 1.9 - need to fix this before merging obviously.

@warrenseen

@dbussink are you able to have a look at this again. The travis failure seems to be due to a crash, specs pass for me fine.

@dbussink dbussink commented on the diff
spec/ruby/core/io/copy_stream_spec.rb
@@ -1,5 +1,6 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
+require 'stringio'
@dbussink Owner
dbussink added a note

We don't want to include specs that depend on stuff in lib/ in spec/ruby/core. If we need specs for StringIO behavior here, they should go in spec/library/stringio

sure, makes sense, although this is more about copy_stream's behaviour with an IO-ish object like stringio.

@dbussink Owner
dbussink added a note

Yeah, I get that this isn't really easy / straightforward in that sense, just that lib/ dependencies inside core specs aren't something we want, especially with goals like gemifying lib etc. that can get very hairy in the future. So we are strict about that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dbussink
Owner

Could you also make sure to separate commits in spec/ruby from other commits?

@dbussink
Owner

Do you think you could update the pull request with the latest feedback?

@warrenseen

Yep, I'll have a look at it over the next couple of days when I have some time free.

@brixen
Owner

I added core specs for arbitrary objects in 2bbacf1 and fixed IO.copy_stream in 0c42833. This works with StringIO.

@brixen brixen closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 29, 2013
  1. @warrenseen

    Add ensure_open_and_* methods to StringIO.

    warrenseen authored
    These methods are required to allow IO.copy_stream to work against a StringIO
    instance.
    
    Alternatively, it may be better to refactor IO.copy_stream to not use these
    methods, as they aren't API-compatible with the MRI IO implementation?
Commits on Apr 1, 2013
  1. @warrenseen
  2. @warrenseen
  3. @warrenseen
  4. @warrenseen
This page is out of date. Refresh to see the latest.
View
15 kernel/common/io19.rb
@@ -280,7 +280,14 @@ def run
saved_pos = 0
end
- @from.seek @offset, IO::SEEK_CUR if @offset
+ if @offset
+ if @from.respond_to?(:descriptor) && @from.descriptor != -1
+ @from.seek @offset, IO::SEEK_CUR
+ else
+ raise ArgumentError, "cannot specify src_offset for non-IO"
+ end
+ end
+
size = @length ? @length : 16384
bytes = 0
@@ -296,7 +303,9 @@ def run
# done reading
end
- @to.flush
+ # Should only flush if @from and @to are both descended from IO
+ # in other words, MRI does not flush copy_stream from/to a StringIO
+ @to.flush if @from.kind_of?(IO) && @to.kind_of?(IO)
return bytes
ensure
if @from_io
@@ -305,7 +314,7 @@ def run
@from.close
end
- @to.close unless @to_io
+ @to.close unless @to_io || @to.closed?
end
end
View
2  lib/19/stringio.rb
@@ -66,6 +66,7 @@ def check_readable
raise IOError, "not opened for reading" unless @readable
end
+ alias_method :ensure_open_and_readable, :check_readable
private :check_readable
def check_writable
@@ -73,6 +74,7 @@ def check_writable
raise IOError, "unable to modify data" if @__data__.string.frozen?
end
+ alias_method :ensure_open_and_writable, :check_writable
private :check_writable
def set_encoding(external, internal=nil, options=nil)
View
36 spec/ruby/core/io/copy_stream_spec.rb
@@ -1,5 +1,6 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
+require 'stringio'
@dbussink Owner
dbussink added a note

We don't want to include specs that depend on stuff in lib/ in spec/ruby/core. If we need specs for StringIO behavior here, they should go in spec/library/stringio

sure, makes sense, although this is more about copy_stream's behaviour with an IO-ish object like stringio.

@dbussink Owner
dbussink added a note

Yeah, I get that this isn't really easy / straightforward in that sense, just that lib/ dependencies inside core specs aren't something we want, especially with goals like gemifying lib etc. that can get very hairy in the future. So we are strict about that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ruby_version_is "1.9" do
describe :io_copy_stream_to_file, :shared => true do
@@ -46,6 +47,7 @@
describe :io_copy_stream_to_io, :shared => true do
it "copies the entire IO contents to the IO" do
IO.copy_stream(@object.from, @to_io)
+ @to_io.flush if @object.flush?
@to_name.should have_data(@content)
IO.copy_stream(@from_bigfile, @to_io)
@to_name.should have_data(@content + @content_bigfile)
@@ -59,6 +61,7 @@
it "starts writing at the destination IO's current position" do
@to_io.write("prelude ")
IO.copy_stream(@object.from, @to_io)
+ @to_io.flush if @object.flush?
@to_name.should have_data("prelude " + @content)
end
@@ -80,6 +83,7 @@
it "copies only length bytes when specified" do
IO.copy_stream(@object.from, @to_io, 8).should == 8
+ @to_io.flush if @object.flush?
@to_name.should have_data("Line one")
end
end
@@ -235,5 +239,37 @@
it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
end
end
+
+ describe "from a string IO" do
+ before :each do
+ @from_io = StringIO.new(@content)
+ IOSpecs::CopyStream.from = @from_io
+ IOSpecs::CopyStream.flush = true
+ end
+
+ describe "to a file name" do
+ it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream
+
+ it "raises an error when an offset is specified" do
+ lambda { IO.copy_stream(@from_io, @to_name, 8, 4) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "to an IO" do
+ before :each do
+ @to_io = new_io @to_name, "w:utf-8"
+ end
+
+ after :each do
+ @to_io.close
+ end
+
+ it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
+
+ it "raises an error when an offset is specified" do
+ lambda { IO.copy_stream(@from_io, @to_io, 8, 4) }.should raise_error(ArgumentError)
+ end
+ end
+ end
end
end
View
10 spec/ruby/core/io/fixtures/classes.rb
@@ -121,7 +121,7 @@ def self.closed_io
io.close
io
end
-
+
# Creates a pipe-based IO fixture containing the specified
# contents
def self.pipe_fixture(content)
@@ -146,5 +146,13 @@ def self.from=(from)
def self.from
@from
end
+
+ def self.flush=(flush)
+ @flush = flush
+ end
+
+ def self.flush?
+ @flush
+ end
end
end
Something went wrong with that request. Please try again.