0

I have a script that writes to a log file. In Python 2, my quick solution to allow tailing/viewing of the log as it progressed was by assigning sys.stdout to a file object with buffering set to 0:

original_stdout = sys.stdout
sys.stdout = open(log_file, 'w', 0)

Once set, any print statements in the script's functions redirect to the log file very nicely.

Running the 2to3-converted version under Python 3 gives the following error: ValueError: can't have unbuffered text I/O. Changing the 'w' above to 'wb' solves that, so the structure of the block is

original_stdout = sys.stdout
sys.stdout = open(log_file, 'wb', 0)
print("{}".format(first_message))

but now the first print statement errors with TypeError: 'str' does not support the buffer interface. I tried explicitly casting the string to bytes

print(bytes("{}".format(first_message), "UTF-8"))

but that produces the same TypeError as before.

What is the easiest way to write unbuffered text to a file in Python 3?

glarue
  • 530
  • 7
  • 20
  • I ran across this issue with the tweepy API recently trying to use it with Python3 and found that with Python3 IO buffers hold bytes while in Python 2 they hold strings. That's why the TypeError. Try running python -u or set env var PYTHONUNBUFFERED=1, see http://stackoverflow.com/questions/107705/disable-output-buffering –  Sep 03 '15 at 02:39

3 Answers3

1

According to Python 3.4.3 documentation at https://docs.python.org/3/library/io.html#raw-i-o and 3.5 documenmtation at https://docs.python.org/3.5/library/io.html#raw-i-o the way to get unbuffered IO is with Raw IO which can be enabled as in:

f = open("myfile.jpg", "rb", buffering=0)

That means "wb" should work for writing.

Details on Raw IO are at https://docs.python.org/3/library/io.html#io.RawIOBase and https://docs.python.org/3.5/library/io.html#io.RawIOBase which appear to be the same.

I did some testing and found buffering of Text IO to be severe and can amount to hundreds of lines and this happens even when writing to sys.stderr and redirecting the error output to a file, on Windows 7 at least. The I tried Raw IO and it worked great! - each line printed came through immediately and in plain text in tail -f output. This is what worked for me on Windows 7 with Python 3.4.3 and using tail bundled with GitHub tools:

import time
import sys
f = open("myfile.txt", "ab", buffering=0)
c = 0
while True:
    f.write(bytes("count is " + str(c) + '\n','utf-8'))
    c += 1
    time.sleep(1)
0

If by unbuffered you mean having the outputs immediately flushed to disk, you can simply do this:

original_stdout = sys.stdout
sys.stdout = open(log_file, 'w')
print(log_message, flush=True)

As print is now a first-class function you can also specify which file to print to, such as:

fd = open(log_file, 'w')
print(log_message, file=fd, flush=True)
metatoaster
  • 17,419
  • 5
  • 55
  • 66
0

The issue seems to be in the way you open the file -

open(log_file, 'w', 0)

From Python 3.x documentation -

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

The third argument for open() determines the buffering mode for the file, 0 means no buffering. I do not think you can make it work by just using 'wb' instead of 'w' .

You should remove that 0 third argument, and let open() use default line buffering for text files. Example -

open(log_file, 'w')
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • 1
    I do not think you can have that along with redirecting to a log file together, in python 3. For running complete python in unbuffered mode you can checkout - http://stackoverflow.com/questions/107705/disable-output-buffering . Or the best would be to use `logging` module, which would give you far better control than redirection of stdout. – Anand S Kumar Sep 03 '15 at 19:58
  • 1
    It looks like logging is definitely the way to go in Python 3. I had avoided rewriting the whole script to use that module until moving from 2 to 3, but it is definitely the most straightforward solution. – glarue Oct 11 '15 at 19:52
  • yea, I believe using the `logging` module would really be better in the long run. – Anand S Kumar Oct 11 '15 at 19:58