admin管理员组

文章数量:1122846

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

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

Share Improve this question edited Nov 23, 2024 at 2:36 Mikkel Rev asked Nov 23, 2024 at 1:47 Mikkel RevMikkel Rev 9013 gold badges14 silver badges31 bronze badges 17
  • 2 Stackoverflow has told me that the best way to do it is by using expect.” While acknowledging that “the best way to do” anything is almost always subjective, I am extremely doubtful that using expect is the most optimal option by any reasonable measure. Can you link the questions from Stack Overflow on which you mention you’re basing this claim? – esqew Commented Nov 23, 2024 at 1:55
  • 3 expect is an appropriate tool when you're automating programs that aren't designed to be automated. It's not necessary or appropriate when you're intentionally designing programs to communicate with each other -- when you're designing your own messaging protocol you can provide guarantees (f/e, that each round of communication will be a single message with a well-defined delimiter following it, and that the programs will alternate rounds) that make expect's functionality unnecessary. – Charles Duffy Commented Nov 23, 2024 at 2:31
  • 1 nod -- ftp was designed to be driven by humans, so using expect when automating interactions with it is appropriate (not as appropriate as using a native Python FTP library, but if we assume you have to use the external ftp program for some reason, expect is a reasonable choice). But if you're designing both the programs, you're in a different situation. – Charles Duffy Commented Nov 23, 2024 at 2:34
  • 1 In your present situation, it makes more sense to just use subprocess.Popen. There's no call for expect. – Charles Duffy Commented Nov 23, 2024 at 2:40
  • 1 You certainly can do repeated communications with subprocess.Popen, you just can't use communicate(). – Charles Duffy Commented Nov 23, 2024 at 2:40
 |  Show 12 more comments

2 Answers 2

Reset to default 1

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.

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

本文标签: automationexpect make two python scripts communicateStack Overflow