Stop Using print() Like It's 2010: Master Python's Rich Library

Stop Using print() Like It’s 2010 — Python’s rich Library Will Change How You Code

Beyond the Basics · Python Toolkit Series · #1


Picture this.

You’ve just written a Python script that downloads 500 files, processes each one, and saves results to a database. You run it. The terminal goes quiet. Is it working? Did it crash? Is it stuck on file #3? You have absolutely no idea.

So you did what every developer does — you sprinkled print() statements everywhere.

print("Starting...")
print("Processing file 1")
print("Processing file 2")
# ... 498 more lines of this
print("Done!")

Now your terminal looks like a phone book. You can’t tell what’s important, what’s an error, and what’s just noise. Scrolling through it feels like searching for a needle in a haystack.

There’s a better way. And it takes one command to unlock it.

pip install rich

What Even Is rich?

Think of rich as a superpower upgrade for your terminal output.

Your terminal right now only shows plain black-and-white text. rich teaches it to show colors, tables, progress bars, loading spinners, syntax-highlighted code, and much more — without you having to learn any terminal magic.

It was created by Will McGugan and has over 55,000 GitHub stars — that’s developers worldwide saying “yes, I need this.” Almost half a million projects use it. It works on Windows, Mac, and Linux, and requires only Python 3.8 or newer.

The best part? You don’t have to rewrite your code. You can start with literally one word change.


The Friendliest Possible Starting Point

Open your terminal, install rich, and run this:

pip install rich
python -m rich

That second command shows you a live demo of everything rich can do. It’s worth running just to see it.

Now, the smallest possible code change:

Before (what everyone writes):

# Boring, plain, forgettable
print("Hello World!")

After (one import swap):

from rich import print   # Same name, way more powerful

print("[bold magenta]Hello[/bold magenta] [green]World![/green] 🎉")

That’s it. rich uses a simple tag system — like HTML but for your terminal. Wrap any text in [bold], [red], [italic], [underline], or combine them like [bold red] — and rich handles the rest.


Real Example #1 — The Student Grade Checker

Let’s say you’re building a simple script to show a student’s grades. Here’s how most beginners write it:

# The old way — works, but ugly
student = "Arjun"
grades = {"Maths": 92, "Science": 78, "English": 85, "History": 61}

print(f"Grades for {student}:")
for subject, grade in grades.items():
    print(f"  {subject}: {grade}")

Output? Just a list of plain text. Nothing stands out. You can’t immediately tell who passed and who needs attention.

Now with rich:

from rich.console import Console
from rich.table import Table

console = Console()

student = "Arjun"
grades = {"Maths": 92, "Science": 78, "English": 85, "History": 61}

# Create a beautiful table
table = Table(title=f"📊 Report Card — {student}", show_header=True, header_style="bold blue")

table.add_column("Subject", style="cyan", width=12)
table.add_column("Grade", justify="center", width=8)
table.add_column("Status", justify="center", width=10)

for subject, grade in grades.items():
    if grade >= 80:
        status = "[bold green]✓ Pass[/bold green]"
        grade_display = f"[green]{grade}[/green]"
    elif grade >= 60:
        status = "[yellow]~ Average[/yellow]"
        grade_display = f"[yellow]{grade}[/yellow]"
    else:
        status = "[bold red]✗ Needs Work[/bold red]"
        grade_display = f"[red]{grade}[/red]"
    
    table.add_row(subject, grade_display, status)

console.print(table)

Now the output is a clean, colored table. Passing grades are green. Average ones are yellow. Failing ones are red. At a glance, you know everything you need to know. No squinting required.


Real Example #2 — The File Downloader with Progress

Remember the problem from the beginning? Downloading 500 files with no feedback? Here’s the rich solution:

The old frustrating way:

import time

files = [f"report_{i}.pdf" for i in range(1, 501)]

print("Starting download...")
for file in files:
    time.sleep(0.01)  # simulate download
    # Is it working? Who knows!
print("Done! (hopefully)")

With rich — your users will actually know what’s happening:

import time
from rich.progress import track
from rich.console import Console

console = Console()
files = [f"report_{i}.pdf" for i in range(1, 501)]

console.print("[bold]Starting download of 500 files...[/bold]")

for file in track(files, description="[green]Downloading files..."):
    time.sleep(0.01)  # your actual download logic goes here

console.print("[bold green]✓ All 500 files downloaded successfully![/bold green]")

track() is magic. You just wrap your list (or any loop) with it, give it a description, and it automatically shows:

  • A live progress bar filling up
  • How many items are done out of the total
  • How fast it’s going
  • An estimated time remaining

One word added (track), and your script goes from black-box to transparent.


Real Example #3 — The API Response Inspector

You’re learning to work with APIs. You make a request and get back a huge, messy JSON response. How do you figure out what’s inside it?

The painful way:

import requests

response = requests.get("https://jsonplaceholder.typicode.com/users/1")
data = response.json()

print(data)
# → {'id': 1, 'name': 'Leanne Graham', 'username': 'Bret', 'email': 'Sincere@april.biz', ...}
# Good luck reading that blob

The rich way — pretty-print any Python object instantly:

import requests
from rich import print   # Just swap this one line!
from rich.console import Console

console = Console()
response = requests.get("https://jsonplaceholder.typicode.com/users/1")
data = response.json()

# Option 1: Just print it — rich auto-formats dicts/lists
print(data)

# Option 2: Use inspect() to explore it like a pro
from rich import inspect
inspect(data)

When you use rich’s print() on a dictionary or list, it automatically formats it with indentation, colors, and clear structure. No more squinting at one-line blobs of JSON.

inspect() goes even further — it shows you the type, all the keys, nested values, and even methods if you’re inspecting a Python object. It’s like having X-ray vision for your data.


Real Example #4 — The Debugging Upgrade

This one is for everyone who has ever typed print("HERE") or print("GOT HERE 2") while debugging.

Here’s a common beginner debugging pattern:

def calculate_discount(price, discount_percent, is_member):
    print("price:", price)             # debugging breadcrumbs
    print("discount:", discount_percent)
    print("is_member:", is_member)
    
    discount = price * (discount_percent / 100)
    if is_member:
        discount += price * 0.05  # extra 5% for members
    
    final_price = price - discount
    print("final:", final_price)
    return final_price

calculate_discount(1000, 20, True)

Works, but messy. Now watch this with rich:

from rich.console import Console

console = Console()

def calculate_discount(price, discount_percent, is_member):
    console.log("Calculating discount...", log_locals=True)
    # ↑ This one line replaces ALL your print debugging!
    # It shows: timestamp, file name, line number, AND a table of every local variable
    
    discount = price * (discount_percent / 100)
    if is_member:
        discount += price * 0.05
    
    final_price = price - discount
    console.log(f"[green]Final price: ₹{final_price:.2f}[/green]")
    return final_price

calculate_discount(1000, 20, True)

console.log() with log_locals=True is the secret weapon. It automatically prints:

  • The timestamp (so you know when each step ran)
  • The file name and line number (so you know where the log came from)
  • A neat table showing the name and value of every variable at that exact moment

No more scattering 10 print statements. One log line tells the whole story.


Real Example #5 — The Loading Spinner

You’re calling an API or running a slow database query. Your user stares at a blank terminal and wonders if your program crashed.

import time
from rich.console import Console

console = Console()

# The old way — dead silence
print("Fetching weather data...")
time.sleep(3)  # your slow API call
print("Done.")
import time
from rich.console import Console

console = Console()

# The rich way — a live spinner shows it's alive
with console.status("[bold cyan]Fetching weather data from API...", spinner="dots"):
    time.sleep(3)  # your slow API call here — spinner runs automatically

console.print("[bold green]✓ Weather data loaded![/bold green]")

The with console.status(...) block runs a spinner animation for exactly as long as the code inside takes. When it finishes, the spinner disappears and you show a success message. Clean, professional, and tells the user exactly what’s happening.

You can even change the spinner style — spinner="dots", spinner="line", spinner="earth" (yes, a rotating globe emoji), and many more.


Real Example #6 — Beautiful Error Messages

When your script crashes, Python’s default traceback looks like this wall of text that intimidates beginners:

Traceback (most recent call last):
  File "app.py", line 15, in <module>
    result = process_data(user_input)
  File "app.py", line 8, in process_data
    return int(data) / divisor
ZeroDivisionError: division by zero

Add two lines to the very top of your script:

from rich.traceback import install
install(show_locals=True)

# Now run your normal code...
def process_data(data, divisor):
    return int(data) / divisor

user_input = "42"
result = process_data(user_input, 0)  # This will crash

Now when it crashes, rich shows:

  • Each error frame with syntax-highlighted code
  • The exact line highlighted in a different color
  • A table showing the value of every local variable at the time of the crash
  • Clean visual separators between each frame

For beginners, this is genuinely life-changing. Instead of guessing what went wrong, you can see the state of your entire program at the moment it broke.


The One-Minute Cheat Sheet

Here are all the patterns you’ll reach for constantly:

# 1. Colored print — zero learning curve
from rich import print
print("[bold red]Error:[/bold red] File not found")
print("[green]Success:[/green] Data saved!")
print("[yellow]Warning:[/yellow] Low disk space")

# 2. Tables — for any structured data
from rich.table import Table
from rich.console import Console
console = Console()
table = Table(title="My Data")
table.add_column("Name")
table.add_column("Value")
table.add_row("Users", "[green]1,204[/green]")
table.add_row("Errors", "[red]3[/red]")
console.print(table)

# 3. Progress bar — for any loop
from rich.progress import track
for item in track(my_list, description="Processing..."):
    process(item)

# 4. Spinner — for slow operations
with console.status("[cyan]Loading..."):
    result = slow_function()

# 5. Better debug logging
console.log("Checkpoint reached", log_locals=True)

# 6. Better tracebacks — put at the TOP of your file
from rich.traceback import install
install(show_locals=True)

“But I’m a Beginner — Do I Really Need This?”

Yes. Maybe more than anyone else.

Here’s why: when you’re learning Python, half your time is spent trying to understand what your code is actually doing. You print variables to see their values. You wonder why a function isn’t running. You stare at error messages you don’t understand.

rich makes all of that dramatically easier:

  • Can’t understand an error? Install rich tracebacks — you’ll see exactly where and why it broke, with your variables shown.
  • Not sure what’s in a variable? Use from rich import print — dicts, lists, and objects get automatically formatted.
  • Script seems frozen? Add track() to your loops — you’ll know exactly how far it’s gotten.
  • Debugging? Replace your print statements with console.log() — timestamps, line numbers, local variables, all in one.

None of this requires experience. It just requires one pip install.


Where rich Lives in the Real World

You might wonder — do real companies actually use this? Absolutely.

  • FastAPI (one of the most popular Python web frameworks) uses rich for its development server output.
  • pip itself (the tool you use to install Python packages) uses rich for its progress bars and error messages.
  • Pytest, Jupyter, Ansible, and dozens of major open-source tools have rich in their dependencies.

When you add rich to your projects, you’re writing code the same way these professional tools do.


Your 3-Step Challenge

Don’t just read this — try it right now.

Step 1 — Install and see the demo:

pip install rich
python -m rich

Step 2 — Open any Python script you’ve written recently. Add this to the top:

from rich.traceback import install
install(show_locals=True)

Run it. If it crashes, see how much more readable the error is.

Step 3 — Find any for loop in your code and wrap it:

from rich.progress import track

for item in track(your_list, description="Working..."):
    # your existing code stays exactly the same

Each step takes under 2 minutes and gives you something immediately useful.


What’s Next?

rich is just the beginning. The same team built Textual — a library that lets you build full interactive apps (like file managers, dashboards, even games) that run entirely inside the terminal, without a web browser. Once you’re comfortable with rich, Textual is a natural next step.


The Bottom Line

You don’t need to be an advanced developer to write code that looks and behaves professionally. rich closes that gap entirely. One library. One install. And your terminal goes from looking like 1995 to looking like a tool you’re proud to demo.

The print() function served you well. But it’s time to level up.

pip install rich

That’s all it takes.


This is post #1 in the Beyond the Basics series — exploring Python libraries that show up in real work.

Enjoyed this? The next post covers pathlib — the modern way to handle files and folders in Python, and why os.path makes everything harder than it needs to be.