Search code examples
pythonstructsubprocesssys

Using stdout and stdin in a loop in python leading to errors


I am using stdout and stdin to communicate information between two python programs. tester.py should pass telemetry data into helper.py and helper.py should return some command to tester.py.

This seems to work when run without a loop, but when I put the code in tester.py inside a loop that updates the telemetry data, helper.py no longer seems able to pass back the correct command. The console print out is as follows:

b'\x00\x00\x00\x00\x01\x00\x00\x00'
0.0
b''
Traceback (most recent call last):
  File "/Users/Advay/Documents/PyCharm/zip_sim/tester.py", line 44, in <module>
    varr = COMMAND_STRUCT.unpack(cmd)
struct.error: unpack requires a buffer of 8 bytes

The tester.py:

import sys
import subprocess
import struct

TELEMETRY_STRUCT = struct.Struct(">fB3s")
COMMAND_STRUCT = struct.Struct(">fB3s")
helper = subprocess.Popen(['python3', 'helper.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

drop = 1
padding = bytes(3)

for i in range(5):

    speed = i

    helper.stdin.write(TELEMETRY_STRUCT.pack(speed, drop, padding))
    helper.stdin.flush()

    cmd = helper.stdout.read(COMMAND_STRUCT.size)
    print(cmd)
    varr = COMMAND_STRUCT.unpack(cmd)
    print(varr[0])

and the helper.py:

import os
import random
import sys
import struct

TELEMETRY_STRUCT = struct.Struct(">fB3s")
COMMAND_STRUCT = struct.Struct(">fB3s")

telemetry = sys.stdin.buffer.read(TELEMETRY_STRUCT.size)
a = TELEMETRY_STRUCT.unpack(telemetry)

command = COMMAND_STRUCT.pack(a[0], 1, bytes(3))

sys.stdout.buffer.write(command)
sys.stdout.buffer.flush()

Any help would be appreciated a lot, I am at a complete loss as to why it. does not work in the loop.


Solution

  • You're trying to send multiple commands from tester.py to helper.py, but helper.py only reads a single command and then exits -- there is no loop that would allow it to continue receiving additional commands from tester.py.

    When you run tester.py, the first loop iteration succeeds, but the subsequent iteration fails because the helper.stdout.read() returns an empty value (because the helper has exited).

    You need to structure your helper.py so that it can receive multiple commands.

    For example:

    import os
    import random
    import sys
    import struct
    
    TELEMETRY_STRUCT = struct.Struct(">fB3s")
    COMMAND_STRUCT = struct.Struct(">fB3s")
    
    while True:
        telemetry = sys.stdin.buffer.read(TELEMETRY_STRUCT.size)
        if not telemetry:
            break
    
        a = TELEMETRY_STRUCT.unpack(telemetry)
    
        command = COMMAND_STRUCT.pack(a[0], 1, bytes(3))
    
        sys.stdout.buffer.write(command)
        sys.stdout.buffer.flush()
    

    With this change, running tester.py results in:

    b'\x00\x00\x00\x00\x01\x00\x00\x00'
    0.0
    b'?\x80\x00\x00\x01\x00\x00\x00'
    1.0
    b'@\x00\x00\x00\x01\x00\x00\x00'
    2.0
    b'@@\x00\x00\x01\x00\x00\x00'
    3.0
    b'@\x80\x00\x00\x01\x00\x00\x00'
    4.0