Skip to content

Commit 0aca447

Browse files
committed
Add signature for Open3.popen2
1 parent deee3ef commit 0aca447

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

stdlib/open3/0/open3.rbs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,131 @@ module Open3
344344
def self?.capture3: (*String, ?stdin_data: String, ?binmode: boolish) -> [String, String, Process::Status]
345345
| (env, *String, ?stdin_data: String, ?binmode: boolish) -> [String, String, Process::Status]
346346

347+
# <!--
348+
# rdoc-file=lib/open3.rb
349+
# - Open3.popen2([env, ] command_line, options = {}) -> [stdin, stdout, wait_thread]
350+
# - Open3.popen2([env, ] exe_path, *args, options = {}) -> [stdin, stdout, wait_thread]
351+
# - Open3.popen2([env, ] command_line, options = {}) {|stdin, stdout, wait_thread| ... } -> object
352+
# - Open3.popen2([env, ] exe_path, *args, options = {}) {|stdin, stdout, wait_thread| ... } -> object
353+
# -->
354+
# Basically a wrapper for [Process.spawn](rdoc-ref:Process.spawn) that:
355+
#
356+
# * Creates a child process, by calling Process.spawn with the given
357+
# arguments.
358+
# * Creates streams `stdin` and `stdout`, which are the standard input and
359+
# standard output streams in the child process.
360+
# * Creates thread `wait_thread` that waits for the child process to exit; the
361+
# thread has method `pid`, which returns the process ID of the child
362+
# process.
363+
#
364+
# With no block given, returns the array `[stdin, stdout, wait_thread]`. The
365+
# caller should close each of the two returned streams.
366+
#
367+
# stdin, stdout, wait_thread = Open3.popen2('echo')
368+
# # => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f58d52dbe98 run>]
369+
# stdin.close
370+
# stdout.close
371+
# wait_thread.pid # => 2263572
372+
# wait_thread.value # => #<Process::Status: pid 2263572 exit 0>
373+
#
374+
# With a block given, calls the block with the three variables (two streams and
375+
# the wait thread) and returns the block's return value. The caller need not
376+
# close the streams:
377+
#
378+
# Open3.popen2('echo') do |stdin, stdout, wait_thread|
379+
# p stdin
380+
# p stdout
381+
# p wait_thread
382+
# p wait_thread.pid
383+
# p wait_thread.value
384+
# end
385+
#
386+
# Output:
387+
#
388+
# #<IO:fd 6>
389+
# #<IO:fd 7>
390+
# #<Process::Waiter:0x00007f58d59a34b0 sleep>
391+
# 2263636
392+
# #<Process::Status: pid 2263636 exit 0>
393+
#
394+
# Like Process.spawn, this method has potential security vulnerabilities if
395+
# called with untrusted input; see [Command
396+
# Injection](rdoc-ref:command_injection.rdoc@Command+Injection).
397+
#
398+
# Unlike Process.spawn, this method waits for the child process to exit before
399+
# returning, so the caller need not do so.
400+
#
401+
# If the first argument is a hash, it becomes leading argument `env` in the call
402+
# to Process.spawn; see [Execution
403+
# Environment](rdoc-ref:Process@Execution+Environment).
404+
#
405+
# If the last argument is a hash, it becomes trailing argument `options` in the
406+
# call to Process.spawn; see [Execution
407+
# Options](rdoc-ref:Process@Execution+Options).
408+
#
409+
# The single required argument is one of the following:
410+
#
411+
# * `command_line` if it is a string, and if it begins with a shell reserved
412+
# word or special built-in, or if it contains one or more metacharacters.
413+
# * `exe_path` otherwise.
414+
#
415+
# **Argument `command_line`**
416+
#
417+
# String argument `command_line` is a command line to be passed to a shell; it
418+
# must begin with a shell reserved word, begin with a special built-in, or
419+
# contain meta characters:
420+
#
421+
# Open3.popen2('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
422+
# Open3.popen2('echo') {|*args| p args } # Built-in.
423+
# Open3.popen2('date > date.tmp') {|*args| p args } # Contains meta character.
424+
#
425+
# Output (similar for each call above):
426+
#
427+
# # => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577dfe410 dead>]
428+
#
429+
# The command line may also contain arguments and options for the command:
430+
#
431+
# Open3.popen2('echo "Foo"') { |i, o, t| o.gets }
432+
# "Foo\n"
433+
#
434+
# **Argument `exe_path`**
435+
#
436+
# Argument `exe_path` is one of the following:
437+
#
438+
# * The string path to an executable to be called.
439+
# * A 2-element array containing the path to an executable and the string to
440+
# be used as the name of the executing process.
441+
#
442+
# Example:
443+
#
444+
# Open3.popen2('/usr/bin/date') { |i, o, t| o.gets }
445+
# # => "Thu Sep 28 09:41:06 AM CDT 2023\n"
446+
#
447+
# Ruby invokes the executable directly, with no shell and no shell expansion:
448+
#
449+
# Open3.popen2('doesnt_exist') { |i, o, t| o.gets } # Raises Errno::ENOENT
450+
#
451+
# If one or more `args` is given, each is an argument or option to be passed to
452+
# the executable:
453+
#
454+
# Open3.popen2('echo', 'C #') { |i, o, t| o.gets }
455+
# # => "C #\n"
456+
# Open3.popen2('echo', 'hello', 'world') { |i, o, t| o.gets }
457+
# # => "hello world\n"
458+
#
459+
# Related:
460+
#
461+
# * Open3.popen2e: Makes the standard input and the merge of the standard
462+
# output and standard error streams of the child process available as
463+
# separate streams.
464+
# * Open3.popen3: Makes the standard input, standard output, and standard
465+
# error streams of the child process available as separate streams.
466+
#
467+
def self?.popen2: (*String, ?unsetenv_others: bool, ?pgroup: true | Integer, ?umask: Integer, ?close_others: bool, ?chdir: String) -> [IO, IO, Process::Waiter]
468+
| (env, *String, ?unsetenv_others: bool, ?pgroup: true | Integer, ?umask: Integer, ?close_others: bool, ?chdir: String) -> [IO, IO, Process::Waiter]
469+
| [T] (*String, ?unsetenv_others: bool, ?pgroup: true | Integer, ?umask: Integer, ?close_others: bool, ?chdir: String) { (IO, IO, Process::Waiter) -> T } -> T
470+
| [T] (env, *String, ?unsetenv_others: bool, ?pgroup: true | Integer, ?umask: Integer, ?close_others: bool, ?chdir: String) { (IO, IO, Process::Waiter) -> T } -> T
471+
347472
# <!--
348473
# rdoc-file=lib/open3.rb
349474
# - Open3.popen3([env, ] command_line, options = {}) -> [stdin, stdout, stderr, wait_thread]

test/stdlib/Open3_test.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ def test_capture3
3939
Open3, :capture3, { 'FOO' => 'BAR' }, "echo $FOO"
4040
end
4141

42+
def test_popen2
43+
assert_send_type "(*::String) -> [ ::IO, ::IO, ::Process::Waiter ]",
44+
Open3, :popen2, 'echo "Foo"'
45+
assert_send_type "(*::String, unsetenv_others: bool) -> [ ::IO, ::IO, ::Process::Waiter ]",
46+
Open3, :popen2, 'env', unsetenv_others: true
47+
assert_send_type "(*::String, close_others: bool) -> [ ::IO, ::IO, ::Process::Waiter ]",
48+
Open3, :popen2, 'env', close_others: true
49+
assert_send_type "(*::String, chdir: ::String) -> [ ::IO, ::IO, ::Process::Waiter ]",
50+
Open3, :popen2, 'echo "Foo"', chdir: '.'
51+
assert_send_type "(::Hash[::String, ::String], ::String) -> [ ::IO, ::IO, ::Process::Waiter ]",
52+
Open3, :popen2, { 'FOO' => 'BAR' }, "echo $FOO"
53+
54+
assert_send_type "(::String) { (::IO, ::IO, ::Process::Waiter) -> [::IO, ::IO, ::Process::Waiter] } -> [::IO, ::IO, ::Process::Waiter]",
55+
Open3, :popen2, 'echo "Foo"' do |*args| args end
56+
assert_send_type "(::Hash[::String, ::String], ::String) { (::IO, ::IO, ::Process::Waiter) -> [::IO, ::IO, ::Process::Waiter] } -> [::IO, ::IO, ::Process::Waiter]",
57+
Open3, :popen2, { 'FOO' => 'BAR' }, 'echo $FOO' do |*args| args end
58+
end
59+
4260
def test_popen3
4361
assert_send_type "(::String) -> [ ::IO, ::IO, ::IO, ::Process::Waiter ]",
4462
Open3, :popen3, 'echo "Foo"'
@@ -78,6 +96,11 @@ def test_capture2e
7896
CustomOpen3.new, :capture2e, 'echo "Foo"'
7997
end
8098

99+
def test_popen2
100+
assert_send_type "(::String) -> [ ::IO, ::IO, ::Process::Waiter ]",
101+
CustomOpen3.new, :popen2, 'echo "Foo"'
102+
end
103+
81104
def test_popen3
82105
assert_send_type "(::String) -> [ ::IO, ::IO, ::IO, ::Process::Waiter ]",
83106
CustomOpen3.new, :popen3, 'echo "Foo"'

0 commit comments

Comments
 (0)