Python Typer快速入门:手把手教你构建To-Do CLI应用

What is Typer

Typer 是一个用于构建强大且易用的命令行应用程序的库,其设计简洁直观,具备出色的可读性。可以将其视为 CLI 版的 FastAPI。

Typer 的主要特性

  • 直观易写:提供优秀的编辑器支持,代码补全随处可见,减少调试时间,使开发更加高效。同时,它的设计注重可读性,易于学习,无需大量查阅文档。
  • 用户友好:自动生成命令行帮助信息,并支持终端自动补全,使最终用户的交互体验更加顺畅。
  • 简洁高效:减少重复代码,在参数声明时支持多个功能点,降低出错概率,提高开发效率。

Typer 快速入门体验

在开始这个项目之前,让我们先简单体验一下 Typer 的基础用法。先使用pip安装typer:

pip install typer

创建main.py,先写一个简单的Hello体验一下:

import typer

def main(name: str, lastname: str = ""):
    """接受用户输入的姓名并打印问候语"""
    print(f"Hello {name} {lastname}")

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

代码解析

  • typer.run(main) 使 main() 函数成为一个 CLI 命令,自动解析参数。
  • name: str是必填参数,需要在命令行提供。
  • lastname: str = ""是可选参数, 命令没有提供 --lastname时会使用默认值。

在命令行中执行以下命令python main.py "Miles" --lastname "Shi" ,输出的结果如下

从这个简单的示例可以看到Typer的几个特点:简单、直观、无需额外处理参数解析


构建To-Do CLI 应用

1.创建项目

首先创建一个目录todo-cli, 在项目根目录下,创建以下文件和文件夹

├── main.py                  # 入口文件,启动 Typer CLI,处理全局选项(如 --version)
├── requirements.txt         
└── todo/                    
    ├── __init__.py          
    ├── cli.py               # 核心 CLI 逻辑,定义 `typer.Typer()` 应用和命令(add, list, done, delete)
    └── todo.json            # 存储todo待办事项, 作为简单的数据库

设置项目环境

然后需要创建一个python虚拟环境,大家可以选择自己喜欢的虚拟环境工具,这边推荐使用venv。

在项目的根目录下执行以下命令

python -m venv todo-cli # create virtual env

激活虚拟环境, 因为之前创建的虚拟环境是在todo-cli项目目录中的,所以激活时也要在项目目录中执行如下命令或者加上绝对路径

todo-cli/Scripts/activate # activate on windwos
source todo-cli/bin/activate # activate on mac

安装依赖

激活虚拟环境后,使用命令pip install typer安装typer

2.添加命令行入口

初始化Typer应用

首先在todo/cli.py中,创建一个Typer的实例,并添加命令行工具的名称APP_NAME和版本号VERSION

import typer
app = typer.Typer()

APP_NAME = "my_todo"
VERSION = "0.1.0"

添加命令和选项

在cli.py中添加main()方法,并使用typer.Option添加--version选项:

def _version_callback(value: bool) -> None:
    """显示应用版本并退出"""
    if value:
        typer.echo(f"{APP_NAME} v{VERSION}")
        raise typer.Exit()

@app.callback()
def main(
    version: bool = typer.Option(
        False, 
        "--version",
        "-v",
        help="Show the application's version and exit.",
        is_eager=True,  # 立即执行,不要求输入子命令
        callback=_version_callback,  # 触发版本回调
    )
):
    """To-Do CLI"""
    return

最后在__main__.py 中,设置应用的入口点

from todo import cli

if __name__ == "__main__":
    cli.app()

完成以上步骤后,我们使用python main.py --version 就能获取命令行应用的名称和版本号

3.处理数据存储

为了让任务数据管理模块化,我们将数据存储逻辑封装到database.py并使用todo.json作为任务存储(该方式适合小型项目或教程),所有任务以JSON格式存储,每个任务包含:

  • task:任务描述(字符串)
  • done:任务状态(布尔值,True 代表已完成,False 代表未完成)

以下是示例:

[
    {
        "task": "完成 Python 练习",
        "done": false
    },
    {
        "task": "阅读 Typer 官方文档",
        "done": true
    },
    {
        "task": "提交项目报告",
        "done": false
    }
]

以下是database.py:

import json
from pathlib import Path
import typer

DEFAULT_DB_FILE = Path(__file__).parent / "todo.json"

def initialize_db():
    """确保 JSON 数据库文件存在"""
    if not DEFAULT_DB_FILE.exists():
        try:
            DEFAULT_DB_FILE.touch(exist_ok=True)
            with DEFAULT_DB_FILE.open("w", encoding="utf-8") as f:
                json.dump([], f)
        except OSError as e:
            typer.echo(f"⚠️ 无法初始化数据库文件: {e}", err=True)

def load_todos():
    """加载 JSON 文件中的待办事项"""
    initialize_db()  # 确保文件存在
    try:
        with DEFAULT_DB_FILE.open("r", encoding="utf-8") as f:
            return json.load(f)
    except json.JSONDecodeError:
        typer.echo("JSON 解析错误", err=True)
        save_todos([])  # 重新初始化
    except Exception as e:
        typer.echo(f"加载任务时发生错误: {e}", err=True)

    return []

def save_todos(todos):
    """保存待办事项到 JSON 文件"""
    try:
        with DEFAULT_DB_FILE.open("w", encoding="utf-8") as f:
            json.dump(todos, f, indent=4, ensure_ascii=False)
    except Exception as e:
        typer.echo(f"无法保存数据: {e}", err=True)
  • DEFAULT_DB_FILE:定义todo.json文件的存储路径,确保数据存放在todo/目录下
  • initialize_db()函数:初始化todo.json文件,若不存在则创建一个空的JSON文件。
  • load_todos()函数:加载todo.json文件中的数据,并返回Todo任务列表。
  • save_todos(todos)函数:将新的任务列表保存到 todo.json 文件。

4.添加命令行指令

现在,我们将为CLI添加一些操作命令(add、list、done、delete)来管理任务。我们可以直接在cli.py定义这些命令,或者将它们拆分到单独的模块中,以便更好地组织代码。

接下来,我们将逐步实现这些命令,并展示它们的实际效果。

列出所有todo(list command)

# 列出任务
@app.command()
def list():
    """列出所有待办事项"""
    todos = load_todos()
    if not todos:
        typer.echo("📌 当前没有待办事项。")
    else:
        for idx, todo in enumerate(todos, start=1):
            status = "✔️" if todo["done"] else "❌"
            typer.echo(f"{idx}. {status} {todo['task']}")
  • 读取 todo.json 中的任务并按序号显示。
  • 如果todos任务列表为空则显示”当前没有待办事项”

运行示例

可以看到list指令从todo.json中获取了所有todo任务

添加任务 (add command)

# 添加任务
@app.command()
def add(task: str):
    """添加一个新的待办事项"""
    todos = load_todos()
    todos.append({"task": task, "done": False})
    save_todos(todos)
    typer.echo(f"✅ 任务已添加: {task}")
  • load_todos()读取已有任务,追加任务后使用save_todos()保存。
  • typer.echo()显示任务添加成功

运行命令

python main.py add "完成 Python 练习"

输出内容

完成任务(done command)

@app.command()
def done(task_number: int):
    """标记指定任务为已完成"""
    todos = load_todos()
    if 0 < task_number <= len(todos):
        todos[task_number - 1]["done"] = True
        save_todos(todos)
        typer.echo(f"🎉 任务 {task_number} 已完成!")
    else:
        typer.echo("⚠️ 任务编号无效。")
  • 根据task_number任务号找到任务并标记为done。
  • 如果编号无效,CLI会进行提示。

运行命令

python main.py done 1

输出结果

删除任务(delete command)

@app.command()
def delete(task_number: int):
    """删除指定任务"""
    todos = load_todos()
    if 0 < task_number <= len(todos):
        removed = todos.pop(task_number - 1)
        save_todos(todos)
        typer.echo(f"🗑️ 任务已删除: {removed['task']}")
    else:
        typer.echo("⚠️ 任务编号无效。")
  • 通过task_number找到任务并将其从 todo.json 中移除并保存
  • 如果编号无效,CLI 会提示错误。

运行示例

python main.py delete 1

输出结果

5.安装命令行

完成所有CLI命令的开发后,我们希望可以能够像ls这样的命令一样能够直接在终端中运行,而不需要python main.py

使用setuptools进行安装

在todo-cli项目根目录中创建setup.py,并添加以下代码

from setuptools import setup, find_packages

setup(
    name="todo-cli",
    version="0.1.0",
    packages=find_packages(),  #自动查找目录下的Python模块
    install_requires=["typer"],
    entry_points={
        "console_scripts": [
            "my_todo=todo.cli:app",
        ],
    },
)
  • install_requires定义项目依赖项,确保安装 typer,否则命令行工具不能运行。
  • entry_points指定my_todo作为 CLI命令,执行todo.cli模块的app实例。
  • packages查找目录下的Python模块
  • name和version执行pip安装后的包名以及版本号

执行以下命令安装

pip install setuptools
pip install --editable .

安装完成后,在todo-cli的虚拟环境中可以直接运行my_todo —help

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值