Embed Python CLI in Ruby process?

As part of a larger project, I'm trying to "embed" an interactive Python interpreter in a Ruby process. I would like to do something like the following:

$ irb
irb(main):001:0> pipe = IO.popen("python", "w+")
=> #<IO:0x7f3dba4977e0>
irb(main):002:0> pipe.puts "print 'hello'"
=> nil
irb(main):003:0> pipe.gets
=> 'hello\n'

Unfortunately, getsit seems to be hanging, but not returning any way out of the Python process. I tried variations of this procedure using open3, using the r+instead mode w+, and a couple of other minor options ( python -uamong them) without success.

Is there a way to establish an interactive connection with the Python shell from Ruby - essentially “wrap” the Python CLI? I use Ruby 1.8.7 (2010-06-23 patchlevel 299) and Python 2.6.6 on an x86_64 machine, although I hope the solutions will be portable (ish) in Python versions.

+5
source share
2 answers

popennot like python terminal, so you are not working interactively. You can force python to start interactively with -i:

IO.popen("python -i", "r+") do |py|
  while cmd = gets
    py.puts cmd
    puts py.gets
  end
end

You may have to work hard to remove the invitation >>>, etc.

EDIT : here is a multi-line friendly version (I keep the code clear and answer the original question):

IO.popen("python -i", "r+") do |py|
  loop do
    fds = IO.select [py, STDIN]
    fds.each do |(fd)|
      case fd
      when nil;    next
      when STDIN;  py.puts gets
      else;        puts py.gets
      end
    end
  end
end
+1
source

It uses an alternative approach using the Ruby pseudo-terminal library. I tested this with ruby ​​1.9 on Linux and MacOS X, it probably won't work on Windows:

require 'pty'

begin
  # stty -echo turns off terminal echo, without it tty input would be repeated
  # on output
  PTY.spawn( "stty -echo; python" ) do |r, w, pid|
    begin

      cmd = nil
      begin
        w.puts cmd if cmd != nil

        # non-blocking read of stdout with 2 seconds timeout
        while IO.select([r], nil, nil, 2)
          print r.getc
        end

      end while cmd = gets

    rescue Errno::EIO
      puts "end of output"
    end
  end
rescue PTY::ChildExited => e
  puts "The child process exited."
end
+2
source

All Articles