Printing and Colors
You can use the normal print()
to show information on the screen:
import typer
def main():
print("Hello World")
if __name__ == "__main__":
typer.run(main)
It will show the output normally:
$ python main.py
Hello World
Use Rich¶
You can also display beautiful and more complex information using Rich.
Install Rich¶
First, you need to install it:
// Rich comes with typer[all]
$ pip install "typer[all]"
---> 100%
Successfully installed typer rich
// Alternatively, you can install Rich independently
$ pip install rich
---> 100%
Successfully installed rich
Use Rich print
¶
For the simplest cases, you can just import print
from rich
and use it instead of the standard print
:
import typer
from rich import print
data = {
"name": "Rick",
"age": 42,
"items": [{"name": "Portal Gun"}, {"name": "Plumbus"}],
"active": True,
"affiliation": None,
}
def main():
print("Here's the data")
print(data)
if __name__ == "__main__":
typer.run(main)
Just with that, Rich will be able to print your data with nice colors and structure:
$ python main.py
Here's the data
<b>{</b>
<font color="#A6E22E">'name'</font>: <font color="#A6E22E">'Rick'</font>,
<font color="#A6E22E">'age'</font>: <font color="#A1EFE4"><b>42</b></font>,
<font color="#A6E22E">'items'</font>: <b>[</b>
<b>{</b><font color="#A6E22E">'name'</font>: <font color="#A6E22E">'Portal Gun'</font><b>}</b>,
<b>{</b><font color="#A6E22E">'name'</font>: <font color="#A6E22E">'Plumbus'</font><b>}</b>
<b>]</b>,
<font color="#A6E22E">'active'</font>: <font color="#A6E22E"><i>True</i></font>,
<font color="#A6E22E">'affiliation'</font>: <font color="#AE81FF"><i>None</i></font>
<b>}</b>
Rich Markup¶
Rich also supports a custom markup syntax to set colors and styles, for example:
import typer
from rich import print
def main():
print("[bold red]Alert![/bold red] [green]Portal gun[/green] shooting! :boom:")
if __name__ == "__main__":
typer.run(main)
$ python main.py
<font color="#F92672"><b>Alert!</b></font> <font color="#A6E22E">Portal gun</font> shooting! ๐ฅ
In this example you can see how to use font styles, colors, and even emojis.
To learn more check out the Rich docs.
Rich Tables¶
The way Rich works internally is that it uses a Console
object to display the information.
When you call Rich's print
, it automatically creates this object and uses it.
But for advanced use cases, you could create a Console
yourself.
import typer
from rich.console import Console
from rich.table import Table
console = Console()
def main():
table = Table("Name", "Item")
table.add_row("Rick", "Portal Gun")
table.add_row("Morty", "Plumbus")
console.print(table)
if __name__ == "__main__":
typer.run(main)
In this example, we create a Console
, and a Table
. And then we can add some rows to the table, and print it.
If you run it, you will see a nicely formatted table:
$ python main.py
โโโโโโโโโณโโโโโโโโโโโโโ
โ<b> Name </b>โ<b> Item </b>โ
โกโโโโโโโโโโโโโโโโโโโโโฉ
โ Rick โ Portal Gun โ
โ Morty โ Plumbus โ
โโโโโโโโโดโโโโโโโโโโโโโ
Rich has many other features, as an example, you can check the docs for:
Typer and Rich¶
If you are wondering what tool should be used for what, Typer is useful for structuring the command line application, with options, arguments, subcommands, data validation, etc.
In general, Typer tends to be the entry point to your program, taking the first input from the user.
Rich is useful for the parts that need to display information. Showing beautiful content on the screen.
The best results for your command line application would be achieved combining both Typer and Rich.
"Standard Output" and "Standard Error"¶
The way printing works underneath is that the operating system (Linux, Windows, macOS) treats what we print as if our CLI program was writing text to a "virtual file" called "standard output".
When our code "prints" things it is actually "writing" to this "virtual file" of "standard output".
This might seem strange, but that's how the CLI program and the operating system interact with each other.
And then the operating system shows on the screen whatever our CLI program "wrote" to that "virtual file" called "standard output".
Standard Error¶
And there's another "virtual file" called "standard error" that is normally only used for errors.
But we can also "print" to "standard error". And both are shown on the terminal to the users.
Info
If you use PowerShell it's quite possible that what you print to "standard error" won't be shown in the terminal.
In PowerShell, to see "standard error" you would have to check the variable $Error
.
But it will work normally in Bash, Zsh, and Fish.
Printing to "standard error"¶
You can print to "standard error" creating a Rich Console
with stderr=True
.
Tip
stderr
is short for "standard error".
Using stderr=True
tells Rich that the output should be shown in "standard error".
import typer
from rich.console import Console
err_console = Console(stderr=True)
def main():
err_console.print("Here is something written to standard error")
if __name__ == "__main__":
typer.run(main)
When you try it in the terminal, it will probably just look the same:
$ python main.py
Here is something written to standard error
"Standard Input"¶
As a final detail, when you type text in your keyboard to your terminal, the operating system also considers it another "virtual file" that you are writing text to.
This virtual file is called "standard input".
What is this for¶
Right now this probably seems quite useless ๐คทโโ.
But understanding that will come handy in the future, for example for autocompletion and testing.
Typer Echo¶
Warning
In most of the cases, for displaying advanced information, it is recommended to use Rich.
You can probably skip the rest of this section. ๐๐
Typer also has a small utility typer.echo()
to print information on the screen, it comes directly from Click. But normally you shouldn't need it.
For the simplest cases, you can use the standard Python print()
.
And for the cases where you want to display data more beautifully, or more advanced content, you should use Rich instead.
Why typer.echo
¶
typer.echo()
(which is actually just click.echo()
) applies some checks to try and convert binary data to strings, and other similar things.
But in most of the cases you wouldn't need it, as in modern Python strings (str
) already support and use Unicode, and you would rarely deal with pure bytes
that you want to print on the screen.
If you have some bytes
objects, you would probably want to decode them intentionally and directly before trying to print them.
And if you want to print data with colors and other features, you are much better off with the more advanced tools in Rich.
Info
typer.echo()
comes directly from Click, you can read more about it in Click's docs.
Color¶
Technical Details
The way color works in terminals is by using some codes (ANSI escape sequences) as part of the text.
So, a colored text is still just a str
.
Tip
Again, you are much better off using Rich for this. ๐
You can create colored strings to output to the terminal with typer.style()
, that gives you str
s that you can then pass to typer.echo()
:
import typer
def main(good: bool = True):
message_start = "everything is "
if good:
ending = typer.style("good", fg=typer.colors.GREEN, bold=True)
else:
ending = typer.style("bad", fg=typer.colors.WHITE, bg=typer.colors.RED)
message = message_start + ending
typer.echo(message)
if __name__ == "__main__":
typer.run(main)
Tip
The parameters fg
and bg
receive strings with the color names for the "foreground" and "background" colors. You could simply pass fg="green"
and bg="red"
.
But Typer provides them all as variables like typer.colors.GREEN
just so you can use autocompletion while selecting them.
Check it:
You can pass these function arguments to typer.style()
:
fg
: the foreground color.bg
: the background color.bold
: enable or disable bold mode.dim
: enable or disable dim mode. This is badly supported.underline
: enable or disable underline.blink
: enable or disable blinking.reverse
: enable or disable inverse rendering (foreground becomes background and the other way round).reset
: by default a reset-all code is added at the end of the string which means that styles do not carry over. This can be disabled to compose styles.
Info
You can read more about it in Click's docs about style()
typer.secho()
- style and print¶
Tip
In case you didn't see above, you are much better off using Rich for this. ๐
There's a shorter form to style and print at the same time with typer.secho()
it's like typer.echo()
but also adds style like typer.style()
:
import typer
def main(name: str):
typer.secho(f"Welcome here {name}", fg=typer.colors.MAGENTA)
if __name__ == "__main__":
typer.run(main)
Check it: