Python To-Do List Application

Python project 02

Python To-Do List Application

Build a simple command-line to-do list that lets a user add tasks, view the list, mark work as complete, and remove tasks that are no longer needed. This project turns basic Python syntax into a small, useful workflow and gives beginners a clean example of how data changes over time inside a program.

Learn Python through guided projectsView all project ideas

What you will build

The application keeps tasks in a list while it is running. A user can type simple commands such as add, view, complete, and remove. Each task has a name and a completion status, so the output makes it easy to see what still needs attention.

Skills practised

  • Lists and dictionaries
  • Functions with clear jobs
  • User input and validation
  • Loops and conditions

Requirements

  • Python 3.8 or later
  • A terminal or command prompt
  • No external libraries
  • About 25 minutes to build and test

Full Python code

Save this program as todo_list_application.py. The code stores each task as a dictionary with two useful pieces of information: its title and whether it has been completed. This keeps the program simple today and leaves room for features such as due dates later.

def show_tasks(tasks):
    """Display all tasks with a clear status and number."""
    if not tasks:
        print("Your list is empty. Add a task to get started.\n")
        return

    print("\nYour tasks:")
    for number, task in enumerate(tasks, start=1):
        status = "Done" if task["completed"] else "Pending"
        print(f"{number}. [{status}] {task['title']}")
    print()


def read_task_number(tasks, prompt):
    """Return a valid task position, or None when there are no tasks."""
    if not tasks:
        print("There are no tasks to choose from.\n")
        return None

    while True:
        value = input(prompt).strip()
        try:
            number = int(value)
            if 1 <= number <= len(tasks):
                return number - 1
        except ValueError:
            pass
        print(f"Enter a task number from 1 to {len(tasks)}.")


def add_task(tasks):
    title = input("New task: ").strip()
    if not title:
        print("A task cannot be empty.\n")
        return
    tasks.append({"title": title, "completed": False})
    print("Task added.\n")


def complete_task(tasks):
    show_tasks(tasks)
    index = read_task_number(tasks, "Task number to mark complete: ")
    if index is not None:
        tasks[index]["completed"] = True
        print("Task marked as complete.\n")


def remove_task(tasks):
    show_tasks(tasks)
    index = read_task_number(tasks, "Task number to remove: ")
    if index is not None:
        removed = tasks.pop(index)
        print(f"Removed: {removed['title']}\n")


def main():
    tasks = []
    print("Python To-Do List")
    print("Commands: add, view, complete, remove, quit\n")

    actions = {
        "add": add_task,
        "view": show_tasks,
        "complete": complete_task,
        "remove": remove_task,
    }

    while True:
        command = input("Command: ").strip().lower()
        if command in {"quit", "q", "exit"}:
            print("To-do list closed. Great work today!")
            break
        if command not in actions:
            print("Choose: add, view, complete, remove, or quit.\n")
            continue
        actions[command](tasks)


if __name__ == "__main__":
    main()

How the application works

The tasks list is created inside main() and passed to the helper functions. Because a list is mutable, changes made by add_task(), complete_task(), or remove_task() are visible when the user chooses view again.

show_tasks() uses enumerate() to display a friendly number beside every task. Internally, Python lists start at position zero, but people usually expect the first item to be number one. The read_task_number() function handles that difference and prevents the program from selecting a task outside the list.

The actions dictionary connects each command to the function that should run. This is a neat alternative to a long chain of conditions and makes it straightforward to add more commands as the project grows.

Run the project

  1. Open a terminal in the folder where you saved the code.
  2. Run python todo_list_application.py. On some Windows systems, use py todo_list_application.py.
  3. Type add and enter a task such as Practice Python.
  4. Use view, then try complete or remove with a task number.
Python To-Do List
Commands: add, view, complete, remove, quit

Command: add
New task: Practice Python
Task added.

Command: view
Your tasks:
1. [Pending] Practice Python

Command: complete
Task number to mark complete: 1
Task marked as complete.

Notes for your notebook

  • Lists: use a list when the order of tasks matters and the number of items can change.
  • Dictionaries: a dictionary keeps related data together. Here, each task has a title and completed value.
  • Validation: always check that a task number is inside the valid range before using it.
  • Program state: this version stores tasks only while the application is open. Saving to a file is a useful next improvement.

Ways to extend this project

Try adding due dates, priorities, categories, or a search command. The next major upgrade is saving tasks to a JSON file so they remain available after the program closes. The Python file handling guide explains the foundations you need for that step. If you want to organise the application into larger reusable parts, review Python classes and objects next.

Where to take your to-do app next

A useful next upgrade is saving tasks between sessions. The Python File Organizer Project gives you more practice with paths and file operations, while the file handling guide explains the CSV and JSON concepts needed for persistence. When your app grows, use classes and objects to group related behaviour. You can also get mentor support through the Python course in Vizag.