Original issue: #9295
While implementing GitHub action runs with parallel_tests on Window server, we got error:
TypeError: no implicit conversion of Hash into String
popen at org/jruby/RubyIO.java:4541
execute_command_and_capture_output at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel_tests-5.5.0/lib/parallel_tests/test/runner.rb:107
execute_command at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel_tests-5.5.0/lib/parallel_tests/test/runner.rb:94
execute_command_in_parallel at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel_tests-5.5.0/lib/parallel_tests/cli.rb:430
execute_in_parallel at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel_tests-5.5.0/lib/parallel_tests/cli.rb:61
call_with_index at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel-1.27.0/lib/parallel.rb:650
work_in_threads at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel-1.27.0/lib/parallel.rb:441
with_instrumentation at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel-1.27.0/lib/parallel.rb:660
work_in_threads at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel-1.27.0/lib/parallel.rb:440
in_threads at C:/a/eazybi/eazybi/vendor/bundle/jruby/3.1.0/gems/parallel-1.27.0/lib/parallel.rb:219
For our case we patched the gem to call not IO.popen(env, cmd, popen_options), but IO.popen([env, *cmd], **popen_options).
Environment Information
Discovered this issue on Windows and JRuby 9.4.13.0, but it's actual also on the master branch and can be reproduced on Unix when the native popen call is disabled with -Xnative.popen=false
Other relevant info you may wish to add:
No gems needed, no framework or environment variables.
Expected Behavior
The following test code:
def test(description)
print "#{description}... "
begin
result = yield
puts "OK (#{result.inspect})"
rescue => e
puts "FAIL: #{e.class}: #{e.message}"
end
end
# 1) Basic popen - no env, no options
test("cmd only") do
IO.popen("echo hello") { |io| io.read.strip }
end
# 2) With options hash only (no env)
test("cmd + options") do
IO.popen("echo hello", err: [:child, :out]) { |io| io.read.strip }
end
# 3) With env hash only (no options)
test("env + cmd") do
IO.popen({"MY_VAR" => "1"}, "echo hello") { |io| io.read.strip }
end
# 4) With both env hash AND options hash
test("env + cmd + options") do
IO.popen({"MY_VAR" => "1"}, "echo hello", err: [:child, :out]) { |io| io.read.strip }
end
# 5) Array command form with env and options
test("env + [cmd] + options") do
IO.popen({"MY_VAR" => "1"}, ["echo", "hello"], err: [:child, :out]) { |io| io.read.strip }
end
# 6) Array of environment and command with options
test("[env, *cmd] + options") do
IO.popen([{"MY_VAR" => "1"}, *["echo", "hello"]], err: [:child, :out]) { |io| io.read.strip }
end
When executed returns all OK as with native popen:
cmd only... OK ("hello")
cmd + options... OK ("hello")
env + cmd... OK ("hello")
env + cmd + options... OK ("hello")
env + [cmd] + options... OK ("hello")
[env, *cmd] + options... OK ("hello")
Actual Behavior
When the test executed on Windows or with native popen disabled:
cmd only... OK ("hello")
cmd + options... test_popen_env_opts.rb:18: warning: unsupported popen option: err
OK ("hello")
env + cmd... OK ("hello")
env + cmd + options... FAIL: TypeError: no implicit conversion of Hash into String
env + [cmd] + options... FAIL: TypeError: no implicit conversion of Hash into String
[env, *cmd] + options... test_popen_env_opts.rb:38: warning: unsupported popen option: err
OK ("hello")
Possible solution
The following code n RubyIO.java popen method is responsible for the parameter processing:
if (argc > 0 && !TypeConverter.checkHashType(runtime, args[0]).isNil()) {
firstArg++;
argc--;
}
if (argc > 0 && !(tmp = TypeConverter.checkHashType(runtime, args[argc - 1])).isNil()) {
options = (RubyHash)tmp;
argc--;
}
if (argc > 1) {
pmode = args[firstArg + 1];
}
- The first block strips environment hash from front ->
firstArg = 1, argc = 2
- Second condition now checks
args[argc-1] == args[1], not the needed args[2].
- As
argc still is 2, the next condition is true and pmode = args[2] which is the given options hash.
- Later
extractModeEncoding is trying to convert options hash to string and the error is raised.
The possible solution could be to change the order of the operations:
if (argc > 1 && !(tmp = TypeConverter.checkHashType(runtime, args[argc - 1])).isNil()) {
options = (RubyHash)tmp;
argc--;
}
if (argc > 0 && !TypeConverter.checkHashType(runtime, args[0]).isNil()) {
firstArg++;
argc--;
}
if (argc > 1) {
pmode = args[firstArg + 1];
}
After applying these changes, the test code now executes with warnings, but that's already different issue:
cmd only... OK ("hello")
cmd + options... test_popen_env_opts.rb:18: warning: unsupported popen option: err
OK ("hello")
env + cmd... OK ("hello")
env + cmd + options... test_popen_env_opts.rb:28: warning: unsupported popen option: err
OK ("hello")
env + [cmd] + options... test_popen_env_opts.rb:33: warning: unsupported popen option: err
OK ("hello")
[env, *cmd] + options... test_popen_env_opts.rb:38: warning: unsupported popen option: err
OK ("hello")
Original issue: #9295
While implementing GitHub action runs with
parallel_testson Window server, we got error:For our case we patched the gem to call not
IO.popen(env, cmd, popen_options), butIO.popen([env, *cmd], **popen_options).Environment Information
Discovered this issue on Windows and JRuby 9.4.13.0, but it's actual also on the master branch and can be reproduced on Unix when the native
popencall is disabled with-Xnative.popen=falseOther relevant info you may wish to add:
No gems needed, no framework or environment variables.
Expected Behavior
The following test code:
When executed returns all OK as with native
popen:Actual Behavior
When the test executed on Windows or with native
popendisabled:Possible solution
The following code n
RubyIO.javapopenmethod is responsible for the parameter processing:firstArg = 1, argc = 2args[argc-1] == args[1], not the neededargs[2].argcstill is 2, the next condition is true andpmode = args[2]which is the given options hash.extractModeEncodingis trying to convert options hash to string and the error is raised.The possible solution could be to change the order of the operations:
After applying these changes, the test code now executes with warnings, but that's already different issue: