Mikkel Rev
Mikkel Rev

Reputation: 901

expect: make two python scripts communicate

I want to have two scripts communicating by exchanging messages. I have to use pexpect because of other restrictions. I am trying to make a minimal working example before I build it out for my application.

I have tried to do a minimal working example by following the tutorials I could find on the internet. But I've failed and here is my attempt.The first script is the one that initiates communication:

#script_1.py

import pexpect

p = pexpect.spawn("python /path/to/script/script_2.py")
p.expect(">")
p.sendline(input("Message to the other script: "))
print( p.before )

Here is the second script which should receive communication and send answer:

#script_2.py

indata = input(">") 
print(indata)

How can I make two python scripts communicate by using pexpect?


EDIT: I was asked why I say that the scripts fail given that there are no error messages. The reason it is a failure is that script 2 should echo the message sent by script 1, and script 1 should print out (or in other ways capture the response), but that doesn't happen

Upvotes: 0

Views: 77

Answers (2)

J_H
J_H

Reputation: 20415

Your difficulty revolves around buffering of the > prompt. The 2nd script does not quickly send it, so the 1st script doesn't see it.

Let us alter the scripts slightly, so they look like this. And assume the user types in "apples".

script_2.py

print("ok1")
indata = input(">")
print("I like", indata, ".")
print("ok2")

script_1.py

import pexpect

p = pexpect.spawn("python /path/to/script_2.py")

p.expect("ok1")
assert p.before == b""
assert p.after == b"ok1"
p.sendline(input("Message to the other script: "))

p.expect("ok2")
assert p.before == b"\r\n>apples\r\nI like apples .\r\n"
assert p.after == b"ok2"
assert p.buffer == b"\r\n"

(I started out with just "ok", then realized I needed two distinct strings.)

What is different, here? The print() with a newline is flushing the buffer to stdout, so that script_1 has an opportunity to see the string it is looking for.

Upvotes: 1

Charles Duffy
Charles Duffy

Reputation: 295242

An implementation of script1 using asyncio -- note that the logging to stderr is for your use as a human reader to follow how the flow of control crosses processes:

#!/usr/bin/env python
import asyncio
import asyncio.subprocess
import sys

# taken from https://stackoverflow.com/a/65326191/14122
async def ainput(string: str) -> str:
    await asyncio.get_event_loop().run_in_executor(None, lambda: sys.stderr.write(string))
    return await asyncio.get_event_loop().run_in_executor(None, sys.stdin.readline)

async def aoutput(string: str) -> None:
    await asyncio.get_event_loop().run_in_executor(None, lambda: sys.stderr.write(string))

async def run():
    p = await asyncio.create_subprocess_exec('./script2.py', stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE)
    while True:
        print("script1: waiting for user input", file=sys.stderr)
        request = await ainput('Message to other script: ')
        print("script1: got user input, writing to script2", file=sys.stderr)
        p.stdin.write((request.rstrip('\n') + '\n').encode())
        await p.stdin.drain()
        print("script1: sent input to script2, waiting for response", file=sys.stderr)
        response = await p.stdout.readline()
        print("script1: got response from script2", file=sys.stderr)
        await aoutput(f'Got response: {response!r}\n')

asyncio.run(run())

...and a version of script2 that loops, to make it worthwhile:

#!/usr/bin/env python3
import sys
while True:
    print("script2: waiting for input on stdin", file=sys.stderr)
    indata = input() # Why would you bother printing a prompt when there's no human?
    print("script2: writing output to stdout", file=sys.stderr)
    print(f'Output data: <{indata!s}>')

When run, output looks like:

$ python3 script1.py
script1: waiting for user input
script2: waiting for input on stdin
I just typed this as the first line of content
Message to other script: script1: got user input, writing to script2
script1: sent input to script2, waiting for response
script2: writing output to stdout
script2: waiting for input on stdin
script1: got response from script2
Got response: b'Output data: <I just typed this as the first line of content>\n'
script1: waiting for user input
I just typed this as the second line of content
Message to other script: script1: got user input, writing to script2
script1: sent input to script2, waiting for response
script2: writing output to stdout
script2: waiting for input on stdin
script1: got response from script2
Got response: b'Output data: <I just typed this as the second line of content>\n'
script1: waiting for user input

Upvotes: 0

Related Questions