/ / Intégrer une interface de ligne de commande Python à un processus Ruby? - python, ruby, sous-processus, pipe, interactif

Intégrer l'interface de ligne de commande Python dans un processus Ruby? - python, ruby, sous-processus, pipe, interactif

Dans le cadre d’un projet plus vaste, j’essaie d’intégrer un interpréteur interactif Python dans un processus Ruby. J’aimerais pouvoir effectuer les tâches suivantes:

$ 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
=> "hellon"

Malheureusement, le gets semble bloquer plutôt que de renvoyer n'importe quel type de sortie du processus Python. J'ai "essayé des variantes de cette procédure avec open3, en utilisant le mode r+ au lieu de w+et quelques autres options mineures (python -u parmi eux), sans succès.

Y a-t-il un moyen d’établir une relation interactive?communication avec un shell Python de Ruby - en effet, pour "envelopper" la CLI Python? J'utilise Ruby 1.8.7 (patch du niveau 29/06/2010 299) et Python 2.6.6 sur un ordinateur x86_64, même si, espérons-le, les solutions seront portables (ish) entre les versions Python.

Réponses:

1 pour la réponse № 1

popen ne ressemble pas à un terminal pour python, vous ne pouvez donc pas exécuter le mode interactif. Vous pouvez forcer le démarrage de python en mode interactif avec -i:

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

Vous devrez probablement travailler pour enlever le >>> invite, etc.

MODIFIER: voici la version multi-lignes conviviale (je conserve le code est clair et répond à la question initiale):

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

2 pour la réponse № 2

Voici une approche alternative utilisant la bibliothèque de pseudo-terminaux de Ruby. J'ai "testé cela avec Ruby 1.9 sur Linux et MacOS X, il ne fonctionnera probablement pas sous 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