pythonsubprocessstdoutstdinpexpect

Multiple inputs and outputs in python subprocess communicate


I need to do something like this post, but I need to create a subprocess that can be given input and give output many times. The accepted answer of that post has good code...

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())

# four
# five

...that I would like to continue like this:

grep_stdout2 = p.communicate(input=b'spam\neggs\nfrench fries\nbacon\nspam\nspam\n')[0]
print(grep_stdout2.decode())

# french fries

But alas, I get the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/subprocess.py", line 928, in communicate
    raise ValueError("Cannot send input after starting communication")
ValueError: Cannot send input after starting communication

The proc.stdin.write() method not enable you to collect output, if I understand correctly. What is the simplest way to keep the lines open for ongoing input/output?

Edit: ====================

It looks like pexpect is a useful library for what I am trying to do, but I am having trouble getting it to work. Here is a more complete explanation of my actual task. I am using hfst to get grammar analyses of individual (Russian) words. The following demonstrates its behavior in a bash shell:

$ hfst-lookup analyser-gt-desc.hfstol
> слово
слово   слово+N+Neu+Inan+Sg+Acc 0.000000
слово   слово+N+Neu+Inan+Sg+Nom 0.000000

> сработай
сработай    сработать+V+Perf+IV+Imp+Sg2 0.000000
сработай    сработать+V+Perf+TV+Imp+Sg2 0.000000

> 

I want my script to be able to get the analyses of one form at a time. I tried code like this, but it is not working.

import pexpect

analyzer = pexpect.spawnu('hfst-lookup analyser-gt-desc.hfstol')
for newWord in ['слово','сработай'] :
    print('Trying', newWord, '...')
    analyzer.expect('> ')
    analyzer.sendline( newWord )
    print(analyzer.before)

# trying слово ...
# 
# trying сработай ...
# слово
# слово слово+N+Neu+Inan+Sg+Acc 0.000000
# слово слово+N+Neu+Inan+Sg+Nom 0.000000
# 
# 

I obviously have misunderstood what pexpect.before does. How can I get the output for each word, one at a time?


Solution

  • This answer should be attributed to @J.F.Sebastian. Thanks for the comments!

    The following code got my expected behavior:

    import pexpect
    
    analyzer = pexpect.spawn('hfst-lookup analyser-gt-desc.hfstol', encoding='utf-8')
    analyzer.expect('> ')
    
    for word in ['слово', 'сработай']:
        print('Trying', word, '...')
        analyzer.sendline(word)
        analyzer.expect('> ')
        print(analyzer.before)