Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Sponsored Reserved space — layout preview until AdSense is connected

Build a Command-Line To-Do List Application with Data Persistence in Python

A persistent command-line to-do list that saves tasks as JSON, supporting add, show, toggle done, and quit commands.

Easy Python 3.9+ Jun 27, 2026 Files & data 1 views 0 copies

Python code

49 lines
Python 3.9+
import json
import os

TODO_FILE = "todos.json"

def load_todos():
    if not os.path.exists(TODO_FILE):
        return []
    with open(TODO_FILE, "r") as f:
        return json.load(f)

def save_todos(todos):
    with open(TODO_FILE, "w") as f:
        json.dump(todos, f, indent=2)

def show_todos(todos):
    if not todos:
        print("No todos yet.")
        return
    for i, todo in enumerate(todos, 1):
        status = "[x]" if todo["done"] else "[ ]"
        print(f"{i}. {status} {todo['task']}")

def add_todo(todos, task):
    todos.append({"task": task, "done": False})

def toggle_todo(todos, index):
    if 1 <= index <= len(todos):
        todos[index - 1]["done"] = not todos[index - 1]["done"]

if __name__ == "__main__":
    todos = load_todos()
    while True:
        print("\nCommands: show, add <task>, done <num>, quit")
        cmd = input("> ").strip()
        if cmd == "quit":
            save_todos(todos)
            break
        elif cmd == "show":
            show_todos(todos)
        elif cmd.startswith("add "):
            add_todo(todos, cmd[4:])
        elif cmd.startswith("done "):
            try:
                toggle_todo(todos, int(cmd.split()[1]))
            except (ValueError, IndexError):
                print("Invalid number.")
        else:
            print("Unknown command.")

Output

stdout
Commands: show, add <task>, done <num>, quit
> add Buy milk
> add Write report
> show
1. [ ] Buy milk
2. [ ] Write report
> done 1
> show
1. [x] Buy milk
2. [ ] Write report
> quit

How it works

The app uses a JSON file (todos.json) for persistent storage across sessions. The load_todos function reads the file if it exists, otherwise returns an empty list. Commands are parsed from user input and modify the in-memory list, and save_todos writes the list back to the file when quitting. Task items are stored as dictionaries with task and done keys, making it easy to extend with fields like priority or due date.

Common mistakes

  • Forgetting to call `save_todos` before exiting, causing data loss.
  • Not handling the case where `todos.json` is malformed (e.g., corrupted JSON).
  • Assuming user input is always well-formed; not catching `ValueError` when parsing numbers.

Variations

  1. Use a SQLite database instead of JSON for more complex queries and multi-user support.
  2. Add subcommands with `argparse` for a more polished CLI experience (e.g., `todo add 'Buy milk'`).

Real-world use cases

  • Personal task management in a terminal without requiring a GUI.
  • A simple project for learning CRUD operations and JSON file I/O in Python.
  • Prototyping a lightweight ticketing or issue tracker for small teams.

Sponsored

Sponsored Reserved space — layout preview until AdSense is connected

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Files & data

Related tutorials and quizzes for this topic.