Search code examples
pythonsubprocess

Python Subprocess Catch STDIN Input and Wait


I'm creating an online python compiler using Django.

I have this code for executing the code

def stream_response():
            try:
                process = subprocess.Popen(['python', temp_code_file_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin = subprocess.PIPE)
                process.stdin.write(b'Hello')
                for line in iter(process.stdout.readline, b''):
                    yield f"data: {line.decode('utf-8')}\n\n"
                    
                for line in iter(process.stdin.readline, b''):
                    yield f"data: {line.decode('utf-8')}\n\n"
                for line in iter(process.stderr.readline, b''):
                    yield f"data: {line.decode('utf-8')}\n\n"
            finally:
                os.unlink(temp_code_file_path)

The streaming process works fine, however, if the file contains an input statement e.g.

print("Hello") 
name = input("Enter your name: ") 
print(name)

The "Enter your name: " is not being streamed. How can I make it in a way that it'll be sent to my client, a specific javascript will run and append an input to the DOM, get the user input, and communicate it to the subprocess STDIN?

I have tried these questions:

python-communicate-with-subprocess-using-stdin

a-non-blocking-read-on-a-subprocess-pipe-in-python

how-do-i-write-to-a-python-subprocess-stdin)

But cant find a solution that will work in my use case


Solution

  • Make sure to close stdin. You could use communicate() to send data to stdin, which closes stdin automatically.

    main.py:

    import subprocess, sys, os
    
    temp_code_file_path = "/tmp/foo.py"
    interpreter = os.path.abspath(sys.executable)
    name_value = "Foobar"
    
    process = subprocess.Popen(
        [interpreter, temp_code_file_path], 
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE, 
        stdin = subprocess.PIPE
    )
    
    process.stdin.write(f"{name_value}\n".encode())
    out, err = process.communicate()
    process.wait()
    
    #Everything from above just in one line:
    #out, err = process.communicate(f"{name_value}\n".encode()) 
    
    
    if process.returncode == 0:
        print("Subprocess successfully executed!")
        print(out.decode())
    else:
        print("ERROR, running subprocess:")
        print(err.decode())
    

    foo.py:

    print("Hello") 
    name = input("Enter your name: ") 
    print(f"Your name is: {name}")
    

    Out:

    Subprocess successfully executed!
    Hello
    Enter your name: Your name is: Foobar