Deprecate Kernel#open and IO support for subprocess creation/forking
Deprecate Kernel#open and IO support for subprocess creation and forking. This deprecates subprocess creation and forking in - Kernel#open - URI.open - IO.binread - IO.foreach - IO.readlines - IO.read - IO.write This behavior is slated to be removed in Ruby 4.0 [Feature #19630]
This commit is contained in:
parent
984109b836
commit
d2343368ab
Notes:
git
2023-08-10 00:38:32 +00:00
@ -8,6 +8,7 @@ They should not be called with unknown or unsanitized commands.
|
|||||||
These methods include:
|
These methods include:
|
||||||
|
|
||||||
- Kernel.system
|
- Kernel.system
|
||||||
|
- Kernel.open
|
||||||
- {\`command` (backtick method)}[rdoc-ref:Kernel#`]
|
- {\`command` (backtick method)}[rdoc-ref:Kernel#`]
|
||||||
(also called by the expression <tt>%x[command]</tt>).
|
(also called by the expression <tt>%x[command]</tt>).
|
||||||
- IO.popen(command).
|
- IO.popen(command).
|
||||||
@ -17,6 +18,7 @@ These methods include:
|
|||||||
- IO.binwrite(command).
|
- IO.binwrite(command).
|
||||||
- IO.readlines(command).
|
- IO.readlines(command).
|
||||||
- IO.foreach(command).
|
- IO.foreach(command).
|
||||||
|
- URI.open(command).
|
||||||
|
|
||||||
Note that some of these methods do not execute commands when called
|
Note that some of these methods do not execute commands when called
|
||||||
from subclass \File:
|
from subclass \File:
|
||||||
|
153
io.c
153
io.c
@ -8109,20 +8109,10 @@ check_pipe_command(VALUE filename_or_command)
|
|||||||
* open(path, mode = 'r', perm = 0666, **opts) -> io or nil
|
* open(path, mode = 'r', perm = 0666, **opts) -> io or nil
|
||||||
* open(path, mode = 'r', perm = 0666, **opts) {|io| ... } -> obj
|
* open(path, mode = 'r', perm = 0666, **opts) {|io| ... } -> obj
|
||||||
*
|
*
|
||||||
* Creates an IO object connected to the given stream, file, or subprocess.
|
* Creates an IO object connected to the given file.
|
||||||
*
|
*
|
||||||
* Required string argument +path+ determines which of the following occurs:
|
* This method has potential security vulnerabilities if called with untrusted input;
|
||||||
*
|
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
||||||
* - The file at the specified +path+ is opened.
|
|
||||||
* - The process forks.
|
|
||||||
* - A subprocess is created.
|
|
||||||
*
|
|
||||||
* Each of these is detailed below.
|
|
||||||
*
|
|
||||||
* <b>File Opened</b>
|
|
||||||
|
|
||||||
* If +path+ does _not_ start with a pipe character (<tt>'|'</tt>),
|
|
||||||
* a file stream is opened with <tt>File.open(path, mode, perm, **opts)</tt>.
|
|
||||||
*
|
*
|
||||||
* With no block given, file stream is returned:
|
* With no block given, file stream is returned:
|
||||||
*
|
*
|
||||||
@ -8139,67 +8129,6 @@ check_pipe_command(VALUE filename_or_command)
|
|||||||
*
|
*
|
||||||
* See File.open for details.
|
* See File.open for details.
|
||||||
*
|
*
|
||||||
* <b>Process Forked</b>
|
|
||||||
*
|
|
||||||
* If +path+ is the 2-character string <tt>'|-'</tt>, the process forks
|
|
||||||
* and the child process is connected to the parent.
|
|
||||||
*
|
|
||||||
* With no block given:
|
|
||||||
*
|
|
||||||
* io = open('|-')
|
|
||||||
* if io
|
|
||||||
* $stderr.puts "In parent, child pid is #{io.pid}."
|
|
||||||
* else
|
|
||||||
* $stderr.puts "In child, pid is #{$$}."
|
|
||||||
* end
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* In parent, child pid is 27903.
|
|
||||||
* In child, pid is 27903.
|
|
||||||
*
|
|
||||||
* With a block given:
|
|
||||||
*
|
|
||||||
* open('|-') do |io|
|
|
||||||
* if io
|
|
||||||
* $stderr.puts "In parent, child pid is #{io.pid}."
|
|
||||||
* else
|
|
||||||
* $stderr.puts "In child, pid is #{$$}."
|
|
||||||
* end
|
|
||||||
* end
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* In parent, child pid is 28427.
|
|
||||||
* In child, pid is 28427.
|
|
||||||
*
|
|
||||||
* <b>Subprocess Created</b>
|
|
||||||
*
|
|
||||||
* If +path+ is <tt>'|command'</tt> (<tt>'command' != '-'</tt>),
|
|
||||||
* a new subprocess runs the command; its open stream is returned.
|
|
||||||
* Note that the command may be processed by shell if it contains
|
|
||||||
* shell metacharacters.
|
|
||||||
*
|
|
||||||
* With no block given:
|
|
||||||
*
|
|
||||||
* io = open('|echo "Hi!"') # => #<IO:fd 12>
|
|
||||||
* print io.gets
|
|
||||||
* io.close
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* "Hi!"
|
|
||||||
*
|
|
||||||
* With a block given, calls the block with the stream, then closes the stream:
|
|
||||||
*
|
|
||||||
* open('|echo "Hi!"') do |io|
|
|
||||||
* print io.gets
|
|
||||||
* end
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* "Hi!"
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
@ -8222,6 +8151,8 @@ rb_f_open(int argc, VALUE *argv, VALUE _)
|
|||||||
else {
|
else {
|
||||||
VALUE cmd = check_pipe_command(tmp);
|
VALUE cmd = check_pipe_command(tmp);
|
||||||
if (!NIL_P(cmd)) {
|
if (!NIL_P(cmd)) {
|
||||||
|
// TODO: when removed in 4.0, update command_injection.rdoc
|
||||||
|
rb_warn_deprecated_to_remove_at(4.0, "Calling Kernel#open with a leading '|'", "IO.popen");
|
||||||
argv[0] = cmd;
|
argv[0] = cmd;
|
||||||
return rb_io_s_popen(argc, argv, rb_cIO);
|
return rb_io_s_popen(argc, argv, rb_cIO);
|
||||||
}
|
}
|
||||||
@ -8259,6 +8190,8 @@ rb_io_open_generic(VALUE klass, VALUE filename, int oflags, int fmode,
|
|||||||
{
|
{
|
||||||
VALUE cmd;
|
VALUE cmd;
|
||||||
if (klass == rb_cIO && !NIL_P(cmd = check_pipe_command(filename))) {
|
if (klass == rb_cIO && !NIL_P(cmd = check_pipe_command(filename))) {
|
||||||
|
// TODO: when removed in 4.0, update command_injection.rdoc
|
||||||
|
rb_warn_deprecated_to_remove_at(4.0, "IO process creation with a leading '|'", "IO.popen");
|
||||||
return pipe_open_s(cmd, rb_io_oflags_modestr(oflags), fmode, convconfig);
|
return pipe_open_s(cmd, rb_io_oflags_modestr(oflags), fmode, convconfig);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -11914,9 +11847,6 @@ io_s_foreach(VALUE v)
|
|||||||
* IO.foreach(path, sep = $/, **opts) {|line| block } -> nil
|
* IO.foreach(path, sep = $/, **opts) {|line| block } -> nil
|
||||||
* IO.foreach(path, limit, **opts) {|line| block } -> nil
|
* IO.foreach(path, limit, **opts) {|line| block } -> nil
|
||||||
* IO.foreach(path, sep, limit, **opts) {|line| block } -> nil
|
* IO.foreach(path, sep, limit, **opts) {|line| block } -> nil
|
||||||
* IO.foreach(command, sep = $/, **opts) {|line| block } -> nil
|
|
||||||
* IO.foreach(command, limit, **opts) {|line| block } -> nil
|
|
||||||
* IO.foreach(command, sep, limit, **opts) {|line| block } -> nil
|
|
||||||
* IO.foreach(...) -> an_enumerator
|
* IO.foreach(...) -> an_enumerator
|
||||||
*
|
*
|
||||||
* Calls the block with each successive line read from the stream.
|
* Calls the block with each successive line read from the stream.
|
||||||
@ -11925,16 +11855,7 @@ io_s_foreach(VALUE v)
|
|||||||
* this method has potential security vulnerabilities if called with untrusted input;
|
* this method has potential security vulnerabilities if called with untrusted input;
|
||||||
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
||||||
*
|
*
|
||||||
* The first argument must be a string that is one of the following:
|
* The first argument must be a string that is the path to a file.
|
||||||
*
|
|
||||||
* - Path: if +self+ is a subclass of \IO (\File, for example),
|
|
||||||
* or if the string _does_ _not_ start with the pipe character (<tt>'|'</tt>),
|
|
||||||
* the string is the path to a file.
|
|
||||||
* - Command: if +self+ is the class \IO,
|
|
||||||
* and if the string starts with the pipe character,
|
|
||||||
* the rest of the string is a command to be executed as a subprocess.
|
|
||||||
* This usage has potential security vulnerabilities if called with untrusted input;
|
|
||||||
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
|
||||||
*
|
*
|
||||||
* With only argument +path+ given, parses lines from the file at the given +path+,
|
* With only argument +path+ given, parses lines from the file at the given +path+,
|
||||||
* as determined by the default line separator,
|
* as determined by the default line separator,
|
||||||
@ -12028,9 +11949,6 @@ io_s_readlines(VALUE v)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* IO.readlines(command, sep = $/, **opts) -> array
|
|
||||||
* IO.readlines(command, limit, **opts) -> array
|
|
||||||
* IO.readlines(command, sep, limit, **opts) -> array
|
|
||||||
* IO.readlines(path, sep = $/, **opts) -> array
|
* IO.readlines(path, sep = $/, **opts) -> array
|
||||||
* IO.readlines(path, limit, **opts) -> array
|
* IO.readlines(path, limit, **opts) -> array
|
||||||
* IO.readlines(path, sep, limit, **opts) -> array
|
* IO.readlines(path, sep, limit, **opts) -> array
|
||||||
@ -12041,19 +11959,7 @@ io_s_readlines(VALUE v)
|
|||||||
* this method has potential security vulnerabilities if called with untrusted input;
|
* this method has potential security vulnerabilities if called with untrusted input;
|
||||||
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
||||||
*
|
*
|
||||||
* The first argument must be a string;
|
* The first argument must be a string that is the path to a file.
|
||||||
* its meaning depends on whether it starts with the pipe character (<tt>'|'</tt>):
|
|
||||||
*
|
|
||||||
* - If so (and if +self+ is \IO),
|
|
||||||
* the rest of the string is a command to be executed as a subprocess.
|
|
||||||
* - Otherwise, the string is the path to a file.
|
|
||||||
*
|
|
||||||
* With only argument +command+ given, executes the command in a shell,
|
|
||||||
* parses its $stdout into lines, as determined by the default line separator,
|
|
||||||
* and returns those lines in an array:
|
|
||||||
*
|
|
||||||
* IO.readlines('| cat t.txt')
|
|
||||||
* # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
|
|
||||||
*
|
*
|
||||||
* With only argument +path+ given, parses lines from the file at the given +path+,
|
* With only argument +path+ given, parses lines from the file at the given +path+,
|
||||||
* as determined by the default line separator,
|
* as determined by the default line separator,
|
||||||
@ -12062,8 +11968,6 @@ io_s_readlines(VALUE v)
|
|||||||
* IO.readlines('t.txt')
|
* IO.readlines('t.txt')
|
||||||
* # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
|
* # => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
|
||||||
*
|
*
|
||||||
* For both forms, command and path, the remaining arguments are the same.
|
|
||||||
*
|
|
||||||
* With argument +sep+ given, parses lines as determined by that line separator
|
* With argument +sep+ given, parses lines as determined by that line separator
|
||||||
* (see {Line Separator}[rdoc-ref:IO@Line+Separator]):
|
* (see {Line Separator}[rdoc-ref:IO@Line+Separator]):
|
||||||
*
|
*
|
||||||
@ -12136,7 +12040,6 @@ seek_before_access(VALUE argp)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* IO.read(command, length = nil, offset = 0, **opts) -> string or nil
|
|
||||||
* IO.read(path, length = nil, offset = 0, **opts) -> string or nil
|
* IO.read(path, length = nil, offset = 0, **opts) -> string or nil
|
||||||
*
|
*
|
||||||
* Opens the stream, reads and returns some or all of its content,
|
* Opens the stream, reads and returns some or all of its content,
|
||||||
@ -12146,18 +12049,7 @@ seek_before_access(VALUE argp)
|
|||||||
* this method has potential security vulnerabilities if called with untrusted input;
|
* this method has potential security vulnerabilities if called with untrusted input;
|
||||||
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
||||||
*
|
*
|
||||||
* The first argument must be a string;
|
* The first argument must be a string that is the path to a file.
|
||||||
* its meaning depends on whether it starts with the pipe character (<tt>'|'</tt>):
|
|
||||||
*
|
|
||||||
* - If so (and if +self+ is \IO),
|
|
||||||
* the rest of the string is a command to be executed as a subprocess.
|
|
||||||
* - Otherwise, the string is the path to a file.
|
|
||||||
*
|
|
||||||
* With only argument +command+ given, executes the command in a shell,
|
|
||||||
* returns its entire $stdout:
|
|
||||||
*
|
|
||||||
* IO.read('| cat t.txt')
|
|
||||||
* # => "First line\nSecond line\n\nThird line\nFourth line\n"
|
|
||||||
*
|
*
|
||||||
* With only argument +path+ given, reads in text mode and returns the entire content
|
* With only argument +path+ given, reads in text mode and returns the entire content
|
||||||
* of the file at the given path:
|
* of the file at the given path:
|
||||||
@ -12169,8 +12061,6 @@ seek_before_access(VALUE argp)
|
|||||||
* unread when encountering certain special bytes. Consider using
|
* unread when encountering certain special bytes. Consider using
|
||||||
* IO.binread if all bytes in the file should be read.
|
* IO.binread if all bytes in the file should be read.
|
||||||
*
|
*
|
||||||
* For both forms, command and path, the remaining arguments are the same.
|
|
||||||
*
|
|
||||||
* With argument +length+, returns +length+ bytes if available:
|
* With argument +length+, returns +length+ bytes if available:
|
||||||
*
|
*
|
||||||
* IO.read('t.txt', 7) # => "First l"
|
* IO.read('t.txt', 7) # => "First l"
|
||||||
@ -12221,7 +12111,6 @@ rb_io_s_read(int argc, VALUE *argv, VALUE io)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* IO.binread(command, length = nil, offset = 0) -> string or nil
|
|
||||||
* IO.binread(path, length = nil, offset = 0) -> string or nil
|
* IO.binread(path, length = nil, offset = 0) -> string or nil
|
||||||
*
|
*
|
||||||
* Behaves like IO.read, except that the stream is opened in binary mode
|
* Behaves like IO.read, except that the stream is opened in binary mode
|
||||||
@ -12326,7 +12215,6 @@ io_s_write(int argc, VALUE *argv, VALUE klass, int binary)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* IO.write(command, data, **opts) -> integer
|
|
||||||
* IO.write(path, data, offset = 0, **opts) -> integer
|
* IO.write(path, data, offset = 0, **opts) -> integer
|
||||||
*
|
*
|
||||||
* Opens the stream, writes the given +data+ to it,
|
* Opens the stream, writes the given +data+ to it,
|
||||||
@ -12336,25 +12224,9 @@ io_s_write(int argc, VALUE *argv, VALUE klass, int binary)
|
|||||||
* this method has potential security vulnerabilities if called with untrusted input;
|
* this method has potential security vulnerabilities if called with untrusted input;
|
||||||
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
* see {Command Injection}[rdoc-ref:command_injection.rdoc].
|
||||||
*
|
*
|
||||||
* The first argument must be a string;
|
* The first argument must be a string that is the path to a file.
|
||||||
* its meaning depends on whether it starts with the pipe character (<tt>'|'</tt>):
|
|
||||||
*
|
*
|
||||||
* - If so (and if +self+ is \IO),
|
* With only argument +path+ given, writes the given +data+ to the file at that path:
|
||||||
* the rest of the string is a command to be executed as a subprocess.
|
|
||||||
* - Otherwise, the string is the path to a file.
|
|
||||||
*
|
|
||||||
* With argument +command+ given, executes the command in a shell,
|
|
||||||
* passes +data+ through standard input, writes its output to $stdout,
|
|
||||||
* and returns the length of the given +data+:
|
|
||||||
*
|
|
||||||
* IO.write('| cat', 'Hello World!') # => 12
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* Hello World!
|
|
||||||
*
|
|
||||||
* With argument +path+ given, writes the given +data+ to the file
|
|
||||||
* at that path:
|
|
||||||
*
|
*
|
||||||
* IO.write('t.tmp', 'abc') # => 3
|
* IO.write('t.tmp', 'abc') # => 3
|
||||||
* File.read('t.tmp') # => "abc"
|
* File.read('t.tmp') # => "abc"
|
||||||
@ -12393,7 +12265,6 @@ rb_io_s_write(int argc, VALUE *argv, VALUE io)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* IO.binwrite(command, string, offset = 0) -> integer
|
|
||||||
* IO.binwrite(path, string, offset = 0) -> integer
|
* IO.binwrite(path, string, offset = 0) -> integer
|
||||||
*
|
*
|
||||||
* Behaves like IO.write, except that the stream is opened in binary mode
|
* Behaves like IO.write, except that the stream is opened in binary mode
|
||||||
|
@ -44,4 +44,14 @@ describe "IO.binread" do
|
|||||||
it "raises an Errno::EINVAL when not passed a valid offset" do
|
it "raises an Errno::EINVAL when not passed a valid offset" do
|
||||||
-> { IO.binread @fname, 0, -1 }.should raise_error(Errno::EINVAL)
|
-> { IO.binread @fname, 0, -1 }.should raise_error(Errno::EINVAL)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.3" do
|
||||||
|
# https://bugs.ruby-lang.org/issues/19630
|
||||||
|
it "warns about deprecation given a path with a pipe" do
|
||||||
|
cmd = "|echo ok"
|
||||||
|
-> {
|
||||||
|
IO.binread(cmd)
|
||||||
|
}.should complain(/IO process creation with a leading '\|'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,10 @@ describe "IO.foreach" do
|
|||||||
platform_is :windows do
|
platform_is :windows do
|
||||||
cmd = "|cmd.exe /C echo hello&echo line2"
|
cmd = "|cmd.exe /C echo hello&echo line2"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.foreach(cmd) { |l| ScratchPad << l }
|
IO.foreach(cmd) { |l| ScratchPad << l }
|
||||||
|
end
|
||||||
ScratchPad.recorded.should == ["hello\n", "line2\n"]
|
ScratchPad.recorded.should == ["hello\n", "line2\n"]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -28,7 +31,9 @@ describe "IO.foreach" do
|
|||||||
it "gets data from a fork when passed -" do
|
it "gets data from a fork when passed -" do
|
||||||
parent_pid = $$
|
parent_pid = $$
|
||||||
|
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.foreach("|-") { |l| ScratchPad << l }
|
IO.foreach("|-") { |l| ScratchPad << l }
|
||||||
|
end
|
||||||
|
|
||||||
if $$ == parent_pid
|
if $$ == parent_pid
|
||||||
ScratchPad.recorded.should == ["hello\n", "from a fork\n"]
|
ScratchPad.recorded.should == ["hello\n", "from a fork\n"]
|
||||||
@ -39,6 +44,16 @@ describe "IO.foreach" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.3" do
|
||||||
|
# https://bugs.ruby-lang.org/issues/19630
|
||||||
|
it "warns about deprecation given a path with a pipe" do
|
||||||
|
cmd = "|echo ok"
|
||||||
|
-> {
|
||||||
|
IO.foreach(cmd).to_a
|
||||||
|
}.should complain(/IO process creation with a leading '\|'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -165,12 +165,19 @@ describe "IO.read from a pipe" do
|
|||||||
platform_is :windows do
|
platform_is :windows do
|
||||||
cmd = "|cmd.exe /C echo hello"
|
cmd = "|cmd.exe /C echo hello"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.read(cmd).should == "hello\n"
|
IO.read(cmd).should == "hello\n"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
platform_is_not :windows do
|
platform_is_not :windows do
|
||||||
it "opens a pipe to a fork if the rest is -" do
|
it "opens a pipe to a fork if the rest is -" do
|
||||||
|
str = nil
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
str = IO.read("|-")
|
str = IO.read("|-")
|
||||||
|
end
|
||||||
|
|
||||||
if str # parent
|
if str # parent
|
||||||
str.should == "hello from child\n"
|
str.should == "hello from child\n"
|
||||||
else #child
|
else #child
|
||||||
@ -185,13 +192,18 @@ describe "IO.read from a pipe" do
|
|||||||
platform_is :windows do
|
platform_is :windows do
|
||||||
cmd = "|cmd.exe /C echo hello"
|
cmd = "|cmd.exe /C echo hello"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.read(cmd, 1).should == "h"
|
IO.read(cmd, 1).should == "h"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
platform_is_not :windows do
|
platform_is_not :windows do
|
||||||
it "raises Errno::ESPIPE if passed an offset" do
|
it "raises Errno::ESPIPE if passed an offset" do
|
||||||
-> {
|
-> {
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.read("|sh -c 'echo hello'", 1, 1)
|
IO.read("|sh -c 'echo hello'", 1, 1)
|
||||||
|
end
|
||||||
}.should raise_error(Errno::ESPIPE)
|
}.should raise_error(Errno::ESPIPE)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -202,11 +214,23 @@ quarantine! do # The process tried to write to a nonexistent pipe.
|
|||||||
# once https://bugs.ruby-lang.org/issues/12230 is fixed.
|
# once https://bugs.ruby-lang.org/issues/12230 is fixed.
|
||||||
it "raises Errno::EINVAL if passed an offset" do
|
it "raises Errno::EINVAL if passed an offset" do
|
||||||
-> {
|
-> {
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.read("|cmd.exe /C echo hello", 1, 1)
|
IO.read("|cmd.exe /C echo hello", 1, 1)
|
||||||
|
end
|
||||||
}.should raise_error(Errno::EINVAL)
|
}.should raise_error(Errno::EINVAL)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.3" do
|
||||||
|
# https://bugs.ruby-lang.org/issues/19630
|
||||||
|
it "warns about deprecation given a path with a pipe" do
|
||||||
|
cmd = "|echo ok"
|
||||||
|
-> {
|
||||||
|
IO.read(cmd)
|
||||||
|
}.should complain(/IO process creation with a leading '\|'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "IO.read on an empty file" do
|
describe "IO.read on an empty file" do
|
||||||
|
@ -180,13 +180,20 @@ describe "IO.readlines" do
|
|||||||
platform_is :windows do
|
platform_is :windows do
|
||||||
cmd = "|cmd.exe /C echo hello&echo line2"
|
cmd = "|cmd.exe /C echo hello&echo line2"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
lines = nil
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
lines = IO.readlines(cmd)
|
lines = IO.readlines(cmd)
|
||||||
|
end
|
||||||
lines.should == ["hello\n", "line2\n"]
|
lines.should == ["hello\n", "line2\n"]
|
||||||
end
|
end
|
||||||
|
|
||||||
platform_is_not :windows do
|
platform_is_not :windows do
|
||||||
it "gets data from a fork when passed -" do
|
it "gets data from a fork when passed -" do
|
||||||
|
lines = nil
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
lines = IO.readlines("|-")
|
lines = IO.readlines("|-")
|
||||||
|
end
|
||||||
|
|
||||||
if lines # parent
|
if lines # parent
|
||||||
lines.should == ["hello\n", "from a fork\n"]
|
lines.should == ["hello\n", "from a fork\n"]
|
||||||
@ -199,6 +206,16 @@ describe "IO.readlines" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.3" do
|
||||||
|
# https://bugs.ruby-lang.org/issues/19630
|
||||||
|
it "warns about deprecation given a path with a pipe" do
|
||||||
|
cmd = "|echo ok"
|
||||||
|
-> {
|
||||||
|
IO.readlines(cmd)
|
||||||
|
}.should complain(/IO process creation with a leading '\|'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it_behaves_like :io_readlines, :readlines
|
it_behaves_like :io_readlines, :readlines
|
||||||
it_behaves_like :io_readlines_options_19, :readlines
|
it_behaves_like :io_readlines_options_19, :readlines
|
||||||
end
|
end
|
||||||
|
@ -215,6 +215,17 @@ describe "IO.write" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.3" do
|
||||||
|
# https://bugs.ruby-lang.org/issues/19630
|
||||||
|
it "warns about deprecation given a path with a pipe" do
|
||||||
|
-> {
|
||||||
|
-> {
|
||||||
|
IO.write("|cat", "xxx")
|
||||||
|
}.should output_to_fd("xxx")
|
||||||
|
}.should complain(/IO process creation with a leading '\|'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,7 +29,9 @@ describe "Kernel#open" do
|
|||||||
|
|
||||||
platform_is_not :windows, :wasi do
|
platform_is_not :windows, :wasi do
|
||||||
it "opens an io when path starts with a pipe" do
|
it "opens an io when path starts with a pipe" do
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
@io = open("|date")
|
@io = open("|date")
|
||||||
|
end
|
||||||
begin
|
begin
|
||||||
@io.should be_kind_of(IO)
|
@io.should be_kind_of(IO)
|
||||||
@io.read
|
@io.read
|
||||||
@ -39,21 +41,27 @@ describe "Kernel#open" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "opens an io when called with a block" do
|
it "opens an io when called with a block" do
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
@output = open("|date") { |f| f.read }
|
@output = open("|date") { |f| f.read }
|
||||||
|
end
|
||||||
@output.should_not == ''
|
@output.should_not == ''
|
||||||
end
|
end
|
||||||
|
|
||||||
it "opens an io for writing" do
|
it "opens an io for writing" do
|
||||||
-> do
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
|
-> {
|
||||||
bytes = open("|cat", "w") { |io| io.write(".") }
|
bytes = open("|cat", "w") { |io| io.write(".") }
|
||||||
bytes.should == 1
|
bytes.should == 1
|
||||||
end.should output_to_fd(".")
|
}.should output_to_fd(".")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
platform_is :windows do
|
platform_is :windows do
|
||||||
it "opens an io when path starts with a pipe" do
|
it "opens an io when path starts with a pipe" do
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
@io = open("|date /t")
|
@io = open("|date /t")
|
||||||
|
end
|
||||||
begin
|
begin
|
||||||
@io.should be_kind_of(IO)
|
@io.should be_kind_of(IO)
|
||||||
@io.read
|
@io.read
|
||||||
@ -63,11 +71,23 @@ describe "Kernel#open" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "opens an io when called with a block" do
|
it "opens an io when called with a block" do
|
||||||
|
suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
@output = open("|date /t") { |f| f.read }
|
@output = open("|date /t") { |f| f.read }
|
||||||
|
end
|
||||||
@output.should_not == ''
|
@output.should_not == ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.3" do
|
||||||
|
# https://bugs.ruby-lang.org/issues/19630
|
||||||
|
it "warns about deprecation given a path with a pipe" do
|
||||||
|
cmd = "|echo ok"
|
||||||
|
-> {
|
||||||
|
open(cmd) { |f| f.read }
|
||||||
|
}.should complain(/Kernel#open with a leading '\|'/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "raises an ArgumentError if not passed one argument" do
|
it "raises an ArgumentError if not passed one argument" do
|
||||||
-> { open }.should raise_error(ArgumentError)
|
-> { open }.should raise_error(ArgumentError)
|
||||||
end
|
end
|
||||||
|
@ -2412,15 +2412,19 @@ class TestIO < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_open_pipe
|
def test_open_pipe
|
||||||
|
assert_deprecated_warning(/Kernel#open with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
|
||||||
open("|" + EnvUtil.rubybin, "r+") do |f|
|
open("|" + EnvUtil.rubybin, "r+") do |f|
|
||||||
f.puts "puts 'foo'"
|
f.puts "puts 'foo'"
|
||||||
f.close_write
|
f.close_write
|
||||||
assert_equal("foo\n", f.read)
|
assert_equal("foo\n", f.read)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_read_command
|
def test_read_command
|
||||||
|
assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
|
||||||
assert_equal("foo\n", IO.read("|echo foo"))
|
assert_equal("foo\n", IO.read("|echo foo"))
|
||||||
|
end
|
||||||
assert_raise(Errno::ENOENT, Errno::EINVAL) do
|
assert_raise(Errno::ENOENT, Errno::EINVAL) do
|
||||||
File.read("|#{EnvUtil.rubybin} -e puts")
|
File.read("|#{EnvUtil.rubybin} -e puts")
|
||||||
end
|
end
|
||||||
@ -2434,9 +2438,11 @@ class TestIO < Test::Unit::TestCase
|
|||||||
Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
|
Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
|
||||||
end
|
end
|
||||||
assert_raise(Errno::ESPIPE) do
|
assert_raise(Errno::ESPIPE) do
|
||||||
|
assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.read("|echo foo", 1, 1)
|
IO.read("|echo foo", 1, 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_reopen
|
def test_reopen
|
||||||
make_tempfile {|t|
|
make_tempfile {|t|
|
||||||
@ -2619,11 +2625,16 @@ class TestIO < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_foreach
|
def test_foreach
|
||||||
a = []
|
a = []
|
||||||
|
|
||||||
|
assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
|
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
|
||||||
|
end
|
||||||
assert_equal(["foo\n", "bar\n", "baz\n"], a)
|
assert_equal(["foo\n", "bar\n", "baz\n"], a)
|
||||||
|
|
||||||
a = []
|
a = []
|
||||||
|
assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
|
||||||
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
|
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
|
||||||
|
end
|
||||||
assert_equal(["zot\n"], a)
|
assert_equal(["zot\n"], a)
|
||||||
|
|
||||||
make_tempfile {|t|
|
make_tempfile {|t|
|
||||||
|
@ -1396,6 +1396,7 @@ EOT
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_open_pipe_r_enc
|
def test_open_pipe_r_enc
|
||||||
|
EnvUtil.suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
open("|#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
|
open("|#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
|
||||||
assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
|
assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
|
||||||
assert_equal(nil, f.internal_encoding)
|
assert_equal(nil, f.internal_encoding)
|
||||||
@ -1404,8 +1405,10 @@ EOT
|
|||||||
assert_equal("\xff".force_encoding("ascii-8bit"), s)
|
assert_equal("\xff".force_encoding("ascii-8bit"), s)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_open_pipe_r_enc2
|
def test_open_pipe_r_enc2
|
||||||
|
EnvUtil.suppress_warning do # https://bugs.ruby-lang.org/issues/19630
|
||||||
open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f|
|
open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f|
|
||||||
assert_equal(Encoding::UTF_8, f.external_encoding)
|
assert_equal(Encoding::UTF_8, f.external_encoding)
|
||||||
assert_equal(nil, f.internal_encoding)
|
assert_equal(nil, f.internal_encoding)
|
||||||
@ -1414,6 +1417,7 @@ EOT
|
|||||||
assert_equal("\u3042", s)
|
assert_equal("\u3042", s)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_s_foreach_enc
|
def test_s_foreach_enc
|
||||||
with_tmpdir {
|
with_tmpdir {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user