Marvin
Posted on November 20, 2021
Overview
If you've been in touch with the Python community relatively recently, you've probably heard of rich
. rich
brings colors to the terminal... without much of a hassle.
These two lines show rich.print
overriding Python's built-in print
function. One thing that caught my attention is rich.print
's docstring, especially this line:
flush (bool, optional): Has no effect as Rich always flushes output. Defaults to False.
I seldomly use this parameter. But I do remember one place where I've seen it — pyautogui
's mouse documentation where Al Sweigart gave this code example which shows the coordinates of your cursor:
#! python3
import pyautogui, sys
print('Press Ctrl-C to quit.')
try:
while True:
x, y = pyautogui.position()
positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4)
print(positionStr, end='')
print('\b' * len(positionStr), end='', flush=True)
except KeyboardInterrupt:
print('\n')
So, really. What does flush
do?
What does flush do?
Without further circumlocutions, let's go ahead and run this script.
flush
does not affect the output. What it changes is the manner in which it is printed.
Here are my findings:
-
flush=False
(yes, the default) will wait for the line to complete before printing it. -
flush=True
will force our terminal to print it. -
flush
does not matter ifend=\n
.
Now this begs the question, why? If I have end=''
, why do I have to explicitly set flush=True
?
print, sys.stdout, and PYTHONUNBUFFERED
Below is the first sentence from Python's print documentation:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
objects
to the text streamfile
, separated bysep
and followed byend
.
To be honest, when I first wrote my Hello World program in Python, I didn't go through the documentation to get awed by its implementation. Now that I've seen it, I can infer that the terminal output is a "text stream" which is sys.stdout
by default.
From the docs:
stdout
is used for the output ofprint()
andexpression statements
and for the prompts ofinput()
...These streams are regular text files like those returned by the
open()
function...When interactive, the stdout stream is line-buffered. Otherwise, it is block-buffered like regular text files. You can make both streams unbuffered by passing the
-u
command-line option or setting thePYTHONUNBUFFERED
environment variable.
There's apparently a deep rabbit-hole to dig into this. More experienced programmers will probably recognize PYTHONUNBUFFERED
in their Dockerfiles. We also probably want to know what's the difference between line-buffered and block-buffered. Personally, I was aware of sys.stdout.write
existing.
But for now, we can be comfortable knowing that terminal output is pretty much like a regular text file being read from somewhere. Hence, it makes sense that print
, by default, would prefer to complete a line first before giving an output.
But how does this affect flush
?
-
Setting PYTHONUNBUFFERED via command line. I use Windows so in PowerShell, it's something like
$env:PYTHONUNBUFFERED = "TRUE"
. This overridesflush=False
. It also overridesos.unsetenv
. - The
os
module (i.e.,os.environ['PYTHONUNBUFFERED'] = "TRUE"
) does NOT overrideflush=False
. -
Via python -u flag. The
-u
flag overridesflush=False
regardless of PYTHONUNBUFFERED value.
Now what?
Let us go back to Al Sweigart's mouse code. This line is really interesting:
print('\b' * len(positionStr), end='', flush=True)
Spoiler alert: you may delete the flush=True
keyword argument. The character \b
is backspace and already enough to delete positionStr
. It's probably there only to show some parallelism between this and the Python 2 code right beneath it. Nevertheless, you may play with this line — change end
, modify len(positionStr)
, you may even change the loop.
But what would flush
do here? Hopefully, this article has given you the first step in the right direction.
List of links
Posted on November 20, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.