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
richfor its development server output. - pip itself (the tool you use to install Python packages) uses
richfor its progress bars and error messages. - Pytest, Jupyter, Ansible, and dozens of major open-source tools have
richin 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.