Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is standard output from subprocess (redirected to unbuffered file) being buffered?

From http://docs.python.org/library/functions.html#open

The optional bufsize argument specifies the file’s desired buffer size: 0 means unbuffered, 1 means line buffered, any other positive value means use a buffer of (approximately) that size. A negative bufsize means to use the system default, which is usually line buffered for tty devices and fully buffered for other files. If omitted, the system default is used.

I'm passing 0 as bufsize below yet without using flush() there's no output written to the file when I run main_process.
What's the reason?

# --------------------------------- sub_process.py
import sys
import time

if __name__ == '__main__':
    print 'printed from redirect.py'
    # why is the following flush() needed? 'std-output' is (?) unbuffered...
    sys.stdout.flush() 
    time.sleep(6)


# --------------------------------- main_process.py
import subprocess
import time

if __name__ == '__main__':
    p = subprocess.Popen(
        ['python', 'sub_process.py'],
        stdout=open('std-output', 'w', 0))
    time.sleep(3)
    p.terminate()
like image 778
Piotr Dobrogost Avatar asked May 07 '11 16:05

Piotr Dobrogost


2 Answers

Use python with the -u flag, e.g.:

if __name__ == '__main__':
    p = subprocess.Popen(
        ['python', '-u', 'sub_process.py'],
        stdout=open('std-output', 'w'))
    time.sleep(3)
    p.terminate()
like image 134
ralphtheninja Avatar answered Oct 04 '22 02:10

ralphtheninja


Extending Magnus Skog solution (+1 by the way :) ):

Well basically what happen is that when subprocess will fork a new process it will duplicate the stdout argument to the new subprocess stdout (fileno = 1) using os.dup2 (look at subprocess.Popen._execute_child) and this will keep the unbuffered state (as what dup2 do), everything until now is good, but when python will be launched (in the subprocess) by default if python don't see the -u flag it will set the buffer of stdout to line buffer (take a look at the main python function.) which will override the buffering flag that you set before.

Hope this explain more the behavior that you was seeing.

like image 22
2 revs Avatar answered Oct 04 '22 04:10

2 revs