Python's print and the flush parameter.
Tags: python, rich, beginners
Published: 2021-11-20 15:10:13

## 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.

{% gist https://gist.github.com/marvintensuan/11ebe670187943f277017e5a7a0e0bb4 file=rich_hello_world.py %}

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 &mdash; `pyautogui`'s mouse [documentation](https://pyautogui.readthedocs.io/en/latest/mouse.html) where Al Sweigart gave this code example which shows the coordinates of your cursor:

```python
    #! 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.

{% gist https://gist.github.com/marvintensuan/11ebe670187943f277017e5a7a0e0bb4 file=buffer.py %}

`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 if `end=\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](https://docs.python.org/3/library/functions.html#print):

> **print**(\*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
> Print `objects` to the text stream `file`, separated by `sep` and followed by `end`.

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](https://docs.python.org/3/library/sys.html#sys.stdout):
> `stdout` is used for the output of `print()` and `expression statements` and for the prompts of `input()`...
>
> 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 the `PYTHONUNBUFFERED` 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 overrides `flush=False`. It also overrides `os.unsetenv`.
- The `os` module (i.e., `os.environ['PYTHONUNBUFFERED'] = "TRUE"`) does **NOT** override `flush=False`.
- __Via python -u flag__. The `-u` flag overrides `flush=False` regardless of PYTHONUNBUFFERED value.

## Now what?

Let us go back to Al Sweigart's mouse code. This line is really interesting:

```python
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 &mdash; 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

- [Gists](https://gist.github.com/marvintensuan/11ebe670187943f277017e5a7a0e0bb4)
- [pyautogui - Mouse Control Functions](https://pyautogui.readthedocs.io/en/latest/mouse.html)
- [Python &mdash; Built-in Functions#print](https://docs.python.org/3/library/functions.html#print)
- [Python &mdash; sys#sys.stdout](https://docs.python.org/3/library/sys.html#sys.stdout)
- [Python &mdash; Command-line and environment#PYTHONUNBUFFERED](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONUNBUFFERED)