Skip to content

Commit 3dd18a6

Browse files
committed
Fix IO.copy_stream with a Tempfile destination
* Fixes #3280
1 parent c6e6162 commit 3dd18a6

File tree

3 files changed

+39
-21
lines changed

3 files changed

+39
-21
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ New features:
66
Bug fixes:
77

88
* Fix `rb_enc_left_char_head()` so it is not always `ArgumentError` (#3267, @eregon).
9+
* Fix `IO.copy_stream` with a `Tempfile` destination (#3280, @eregon).
910

1011
Compatibility:
1112

spec/ruby/core/io/copy_stream_spec.rb

+27-6
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@
6969
end
7070

7171
it "raises an IOError if the destination IO is not open for writing" do
72-
@to_io.close
73-
@to_io = new_io @to_name, "r"
74-
-> { IO.copy_stream @object.from, @to_io }.should raise_error(IOError)
72+
to_io = new_io __FILE__, "r"
73+
begin
74+
-> { IO.copy_stream @object.from, to_io }.should raise_error(IOError)
75+
ensure
76+
to_io.close
77+
end
7578
end
7679

7780
it "does not close the destination IO" do
@@ -109,7 +112,8 @@
109112
end
110113

111114
after :each do
112-
rm_r @to_name, @from_bigfile
115+
rm_r @to_name if @to_name
116+
rm_r @from_bigfile
113117
end
114118

115119
describe "from an IO" do
@@ -164,6 +168,25 @@
164168
it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
165169
it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
166170
end
171+
172+
describe "to a Tempfile" do
173+
before :all do
174+
require 'tempfile'
175+
end
176+
177+
before :each do
178+
@to_io = Tempfile.new("rubyspec_copy_stream", encoding: Encoding::BINARY, mode: File::RDONLY)
179+
@to_name = @to_io.path
180+
end
181+
182+
after :each do
183+
@to_io.close!
184+
@to_name = nil # do not rm_r it, already done by Tempfile#close!
185+
end
186+
187+
it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
188+
it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
189+
end
167190
end
168191

169192
describe "from a file name" do
@@ -277,10 +300,8 @@
277300
@io.should_not_receive(:pos)
278301
IO.copy_stream(@io, @to_name)
279302
end
280-
281303
end
282304

283-
284305
describe "with a destination that does partial reads" do
285306
before do
286307
@from_out, @from_in = IO.pipe

src/main/ruby/truffleruby/core/io.rb

+11-15
Original file line numberDiff line numberDiff line change
@@ -356,24 +356,20 @@ def initialize(from, to, length, offset)
356356
@method = read_method @from
357357
end
358358

359+
# From copy_stream_body in io.c in CRuby
360+
# The first element is true if obj can be used as an IO directly
359361
def to_io(obj, mode)
360-
if Primitive.is_a?(obj, IO)
361-
flag = true
362-
io = obj
362+
has_to_path = obj.respond_to? :to_path
363+
io = (Primitive.is_a?(obj, IO) || has_to_path) && IO.try_convert(obj)
364+
if io
365+
[true, io]
366+
elsif Primitive.is_a?(obj, String) or has_to_path
367+
path = Truffle::Type.coerce_to obj, String, :to_path
368+
io = File.open path, mode
369+
[false, io]
363370
else
364-
flag = false
365-
366-
if Primitive.is_a?(obj, String)
367-
io = File.open obj, mode
368-
elsif obj.respond_to? :to_path
369-
path = Truffle::Type.coerce_to obj, String, :to_path
370-
io = File.open path, mode
371-
else
372-
io = obj
373-
end
371+
[false, obj]
374372
end
375-
376-
[flag, io]
377373
end
378374

379375
def read_method(obj)

0 commit comments

Comments
 (0)