Python Typer Tutorial: Build CLIs with Python in Minutes
Learn how to build CLIs with Python using Typer in a few simple steps.
Image by Author | Created on Canva
In this tutorial, we'll build a Command Line Interface (CLI) using Typer, a Python library that makes building CLIs fast and simple. Typer also leverages Python's type hints.
This tutorial will walk you through how to build a CLI for a schedule tracker. Specifically, we’ll learn how to:
- Set up Typer
- Add commands
- Handle task, time, and priority—input and data types correctly
Let’s get started!
Step 1: Install Typer
First, let's install Typer:
$ pip3 install typer
Installing Typer automatically installs all the required dependencies, which facilitates the command-line interface functionality. Please install Typer in a virtual environment for the project.
Here’s the GitHub repository for this simple project.
Step 2: Create the Basic CLI
Let’s start by creating a simple CLI that can add a task to a schedule with the correct time format.
Create a Python file named schedule_tracker.py
.
Import the required libraries and modules. The typer.Typer()
instance (app
) manages the CLI commands.
import typer
from datetime import datetime
import json
from typing import List
app = typer.Typer()
We define a few helper functions to load the schedule from and save it to a JSON file:
SCHEDULE_FILE = "schedule.json"
def load_schedule() -> List[dict]:
try:
with open(SCHEDULE_FILE, "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_schedule(schedule: List[dict]):
with open(SCHEDULE_FILE, "w") as f:
json.dump(schedule, f, default=str, indent=4)
Because we have a time associated with each task, let’s create a helper function that parses the string to a valid time object:
def parse_time(t: str) -> datetime.time:
try:
return datetime.strptime(t, "%H:%M").time()
except ValueError:
typer.echo("Invalid time format. Please use HH:MM.")
raise typer.Exit()
Create the basic add_task
command. The task (a description of the task), the time, and priority define each task item:
@app.command()
def add_task(
task: str,
time: str = typer.Option(..., help="Time in HH:MM format"),
priority: int = typer.Option(1, help="Priority of the task")
):
schedule = load_schedule()
task_time = parse_time(time)
schedule.append({"task": task, "time": task_time.strftime("%H:%M"), "priority": priority})
save_schedule(schedule)
typer.echo(f"Task '{task}' added at {task_time.strftime('%H:%M')} with priority {priority}")
if __name__ == "__main__":
app()
Step 3: Run the CLI
Now run schedule_tracker.py
to add tasks like so:
$ python3 schedule_tracker.py add-task "Team meeting" --time "14:00" --priority 2
Which outputs:
Task 'Team meeting' added at 14:00 with priority 2
Now you have a working CLI that accepts tasks with associated time and priority!
Step 4: Add More Commands
Let’s add more functionality to our schedule tracker, such as viewing and removing tasks.
First, add a command to view tasks sorted by time:
@app.command()
def list_tasks():
"""List all scheduled tasks."""
schedule = load_schedule()
if not schedule:
typer.echo("No tasks found.")
return
for task in sorted(schedule, key=lambda x: x["time"]):
@app.command()
print(f"Task {i}: {task['task']}")
print(f" Time: {task['time']}")
print(f" Priority: {task['priority']}")
print("-" * 40) # Add a separator line for better readability
You can modify this function as needed to sort tasks by priority as well.
Let’s also add a command to remove tasks by their index number:
@appcommand()
def remove_task(task_number: int):
"""Remove a task by its number."""
schedule = load_schedule()
if 0 < task_number <= len(schedule):
removed_task = schedule.pop(task_number - 1)
save_schedule(schedule) # Save the updated schedule
typer.echo(f"Removed task '{removed_task['task']}' scheduled at {removed_task['time']}")
else:
typer.echo(f"Invalid task number. Choose a number between 1 and {len(schedule)}.")
Step 5: Test the Improved CLI
Now, test the following commands.
Add multiple tasks:
$ python3 schedule_tracker.py add-task "Code review" --time "10:30" --priority 1
$ python3 schedule_tracker.py add-task "Client meeting" --time "12:00" --priority 3
Doing so, you'll see:
Task 'Code review' added at 10:30 with priority 1
Task 'Client meeting' added at 12:00 with priority 3
View all tasks:
$ python3 schedule_tracker.py list-tasks
Running this command outputs:
Task 0: Code review
Time: 10:30
Priority: 1
----------------------------------------
Task 1: Client meeting
Time: 12:00
Priority: 3
----------------------------------------
Task 2: Team meeting
Time: 14:00
Priority: 2
----------------------------------------
Remove a task:
$ python3 schedule_tracker.py remove-task 1
This outputs:
Removed task 'Team meeting' scheduled at 14:00
View the updated task list:
$ python3 schedule_tracker.py list-tasks
We should now see the following output:
Task 0: Code review
Time: 10:30
Priority: 1
----------------------------------------
Task 1: Client meeting
Time: 12:00
Priority: 3
----------------------------------------
Wrapping Up
That’s all for this tutorial. As seen, with Typer, you can create simple yet powerful CLIs in just minutes. In this tutorial, we built a schedule tracker that handles tasks and time input validation efficiently.
You can now improve the version we’ve built. You can extend this project by adding more features like recurring tasks, reminders, or exporting the schedule. Or you can try building a similar app for tracking expenses.
Happy coding!
Bala Priya C is a developer and technical writer from India. She likes working at the intersection of math, programming, data science, and content creation. Her areas of interest and expertise include DevOps, data science, and natural language processing. She enjoys reading, writing, coding, and coffee! Currently, she's working on learning and sharing her knowledge with the developer community by authoring tutorials, how-to guides, opinion pieces, and more. Bala also creates engaging resource overviews and coding tutorials.