These are the libraries that solve small but frustrating problems — things you don't notice until you lose half a day fixing them.

Here are 9 of my favorites, explained the way you'll actually use them: problem → solution → automation.

1) Problem: Debugging With Print Statements Is Chaos

We've all been there. You sprinkle print() everywhere, only to drown in plain white text. Tomorrow morning, you'll look at that log file and wonder if a cat walked across your keyboard.

Solution: rich

A one-stop shop for beautiful terminal output: colors, tables, progress bars, even Markdown. Suddenly, your logs are readable and structured.

Implementation + Example Use Case

Steps:

  1. Install with pip install rich.
  2. Replace print() with Console().print().
  3. Add styles and formatting for clarity.
from rich.console import Console
console = Console()
console.print("[bold cyan]Automation complete![/bold cyan]")
console.print({"status": "success", "records": 120})

Where to use it:

  • Debugging ETL jobs (highlight failed rows).
  • Scrapers (show progress in one color, errors in red).
  • Terminal dashboards.

Once you use rich, you'll never go back to plain print.

2) Problem: Long Loops Look Like Frozen Scripts

Your script is crunching through 10,000 files. Nothing prints for 20 minutes. Is it alive? Dead? Do you dare Ctrl+C?

Solution: tqdm

tqdm wraps your loop in a progress bar—showing you exactly how far along it is, plus an ETA.

Implementation + Example Use Case

Steps:

  1. Install with pip install tqdm.
  2. Wrap your iterable with tqdm().
  3. Done — your loop is now alive.
from tqdm import tqdm
import time

for i in tqdm(range(1000)):
    time.sleep(0.01)

Where to use it:

  • File processing pipelines.
  • Large scrapers.
  • Model training.

No more guessing games — just peace of mind.

3) Problem: Scheduling Jobs With Crontab Is a Pain

Setting up cron is fiddly, system-dependent, and not very portable. You just want your Python script to run every 10 minutes—without fighting Linux.

Solution: schedule

A human-friendly job scheduler in pure Python.

Implementation + Example Use Case

Steps:

  1. Install with pip install schedule.
  2. Define a function.
  3. Schedule it with natural language intervals.
import schedule, time

def job():
    print("Automated task running...")

schedule.every(10).minutes.do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

Where to use it:

  • Auto-backups every night.
  • Email reminders hourly.
  • Refreshing a local dataset.

Now your script has its own mini-cron, minus the pain.

4) Problem: Python's Built-in Logging Feels Like Taxes

The logging module works… but configuring it feels like doing paperwork just to log "Hello world."

Solution: loguru

Drop-in logging with sane defaults, better formatting, and file rotation out of the box.

Implementation + Example Use Case

Steps:

  1. Install with pip install loguru.
  2. Replace print() with logger.info().
  3. Enjoy clean, structured logs.
from loguru import logger
logger.info("Script started")
logger.error("Something went wrong")

Where to use it:

  • Long-running automation.
  • Apps with rotating logs.
  • Anything where print() isn't enough.

Logging without headaches.

5) Problem: Writing Command-Line Tools Is Too Much Work

You want to add a CLI to your script. But argparse feels ancient, and click requires too much boilerplate.

Solution: typer

A modern, type-safe CLI builder. Functions + type hints = instant command-line app.

Implementation + Example Use Case

Steps:

  1. Install with pip install typer.
  2. Write a function with type hints.
  3. Use typer.run().
import typer

def main(name: str):
    print(f"Hello {name}")

if __name__ == "__main__":
    typer.run(main)

Where to use it:

  • Quick utilities.
  • Scripts for teammates.
  • Automation tools with arguments.

Your boring script just became a professional CLI.

6) Problem: Hardcoding Secrets in Scripts (Yikes)

api_key = "abcd1234" inside a script? That's how you accidentally leak keys on GitHub.

Solution: python-dotenv

Keeps secrets safe in .env files and loads them automatically.

Implementation + Example Use Case

Steps:

  1. Create .env with API_KEY=abcd1234.
  2. Install with pip install python-dotenv.
  3. Load variables inside your script.
from dotenv import load_dotenv
import os

load_dotenv()
api_key = os.getenv("API_KEY")

Where to use it:

  • Connecting to APIs.
  • Local dev environments.
  • Sharing configs without exposing keys.

Cleaner, safer, GitHub-proof code.

7) Problem: Garbage Data Breaks Your Script

A user sends { "id": "abc", "name": 123 }. Your script crashes. Not fun.

Solution: pydantic

Define data models once, and let Pydantic validate everything.

Implementation + Example Use Case

Steps:

  1. Install with pip install pydantic.
  2. Create a model with type hints.
  3. Pydantic enforces the rules.
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str

u = User(id=1, name="Alice")  # works

Where to use it:

  • Validating API inputs.
  • Cleaning CSV data.
  • Ensuring your script doesn't choke on bad input.

One line of defense against messy data.

8) Problem: datetime Feels Like Punishment

Python's datetime makes you cry over time zones and formatting. Ever tried adding 3 days to datetime.utcnow()? Yeah.

Solution: arrow

Human-friendly dates and times that don't fight you.

Implementation + Example Use Case

Steps:

  1. Install with pip install arrow.
  2. Use arrow.now() for the current time.
  3. Shift and format effortlessly.
import arrow
print(arrow.now().shift(days=+3).format("YYYY-MM-DD"))

Where to use it:

  • Deadlines and reminders.
  • Scheduling events.
  • Handling time zones without tears.

Time math you can actually read.

9) Problem: Hardcoding Script Arguments Is Inefficient

You keep editing variables inside your script before running it. Over and over.

Solution: fire

Turn any Python function into a CLI instantly — no boilerplate.

Implementation + Example Use Case

Steps:

  1. Install with pip install fire.
  2. Wrap your function with fire.Fire().
  3. Pass arguments from the terminal.
import fire

def greet(name="World"):
    return f"Hello {name}"

if __name__ == '__main__':
    fire.Fire(greet)

Run:

python script.py --name="Alice"

Where to use it:

  • Scripts with changing parameters.
  • Experiments where you tweak values often.
  • Reusable automation tools.

No more hardcoding — just flexibility.

Wrapping Up

These 9 tiny libraries won't make front-page news, but they'll quietly save you hours.

Big frameworks impress on LinkedIn. Tiny utilities keep your scripts alive at 2 a.m.

As Donald Knuth said:

"Programs are meant to be read by humans and only incidentally for computers to execute."

And these small tools? They make your code far more human.

A message from our Founder

Hey, Sunil here. I wanted to take a moment to thank you for reading until the end and for being a part of this community.

Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don't receive any funding, we do this to support the community. ❤️

If you want to show some love, please take a moment to follow me on LinkedIn, TikTok, Instagram. You can also subscribe to our weekly newsletter.

And before you go, don't forget to clap and follow the writer️!