Python 命令行工具开发完全指南:从零到发布

96 阅读9分钟

前言

在日常开发中,我们经常需要创建一些小工具来提高工作效率。如果能将这些工具打包成可以通过 pip install 安装的命令行工具,就能在任何地方轻松使用。本文将详细介绍如何从零开始创建一个专业的Python命令行工具,并通过pip进行安装和分发。

项目概述

我们将创建一个名为 MyTasks 的任务管理命令行工具,它具备以下功能:

  • ✅ 添加、完成、删除任务
  • 🔍 搜索任务
  • 📊 统计信息
  • 🎯 优先级管理
  • 💾 本地JSON存储
  • 🌈 彩色输出

最终效果:

# 本地开发安装
pip install -e .

# 使用工具
mytasks add "学习Python" -d "完成CLI教程" -p high
mytasks list
mytasks complete 1

重要说明: 由于 mytasks 可能与PyPI上的现有包名冲突,本教程重点演示本地开发和安装流程。如需发布到PyPI,请选择唯一的包名。

第一步:项目结构设计

创建项目目录结构:

my_cli/
├── demo.py              # 主程序文件
├── setup.py             # 传统安装配置
├── pyproject.toml       # 现代安装配置
├── README.md            # 项目说明
├── LICENSE              # 许可证
├── requirements.txt     # 依赖列表
└── demo_installation.py # 演示脚本

第二步:核心代码实现

2.1 主程序文件 (demo.py)

首先创建核心功能模块:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
命令行工具演示 - 使用 pip 安装的 CLI 应用
"""

import argparse
import sys
import os
import json
from pathlib import Path
from typing import List, Dict, Any

# 解决Windows下的Unicode显示问题
if sys.platform == "win32":
    import codecs
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
    sys.stderr = codecs.getwriter("utf-8")(sys.stderr.detach())

class TaskManager:
    """任务管理器核心类"""
    
    def __init__(self, data_file: str = None):
        self.data_file = data_file or os.path.expanduser("~/.tasks.json")
        self.tasks = self._load_tasks()
    
    def _load_tasks(self) -> List[Dict[str, Any]]:
        """从JSON文件加载任务数据"""
        if os.path.exists(self.data_file):
            try:
                with open(self.data_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError):
                return []
        return []
    
    def _save_tasks(self):
        """保存任务数据到JSON文件"""
        try:
            os.makedirs(os.path.dirname(self.data_file), exist_ok=True)
            with open(self.data_file, 'w', encoding='utf-8') as f:
                json.dump(self.tasks, f, ensure_ascii=False, indent=2)
        except IOError as e:
            print(f"❌ 保存失败: {e}")
            sys.exit(1)
    
    def add_task(self, title: str, description: str = "", priority: str = "medium"):
        """添加新任务"""
        task = {
            "id": len(self.tasks) + 1,
            "title": title,
            "description": description,
            "priority": priority,
            "completed": False,
            "created_at": self._get_timestamp()
        }
        self.tasks.append(task)
        self._save_tasks()
        print(f"✅ 任务已添加: {title}")
    
    def list_tasks(self, show_completed: bool = False):
        """列出任务"""
        if not self.tasks:
            print("📝 暂无任务")
            return
        
        print("\n📋 任务列表:")
        print("-" * 60)
        
        for task in self.tasks:
            if not show_completed and task["completed"]:
                continue
            
            status = "✅" if task["completed"] else "⏳"
            priority_icon = self._get_priority_icon(task["priority"])
            
            print(f"{status} [{task['id']}] {priority_icon} {task['title']}")
            if task["description"]:
                print(f"    📝 {task['description']}")
            print(f"    🕒 {task['created_at']}")
            print()
    
    def complete_task(self, task_id: int):
        """标记任务为已完成"""
        for task in self.tasks:
            if task["id"] == task_id:
                task["completed"] = True
                task["completed_at"] = self._get_timestamp()
                self._save_tasks()
                print(f"✅ 任务已完成: {task['title']}")
                return
        print(f"❌ 未找到任务 ID: {task_id}")
    
    def _get_priority_icon(self, priority: str) -> str:
        """获取优先级对应的图标"""
        icons = {
            "high": "🔴",
            "medium": "🟡", 
            "low": "🟢"
        }
        return icons.get(priority, "⚪")
    
    def _get_timestamp(self) -> str:
        """获取当前时间戳"""
        from datetime import datetime
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

2.2 命令行参数解析

使用 argparse 创建专业的命令行界面:

def create_parser() -> argparse.ArgumentParser:
    """创建命令行参数解析器"""
    parser = argparse.ArgumentParser(
        prog="mytasks",
        description="🚀 简单易用的任务管理命令行工具",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  mytasks add "学习Python" -d "完成Django教程" -p high
  mytasks list
  mytasks complete 1
  mytasks search "Python"
        """
    )
    
    # 版本信息
    parser.add_argument("--version", action="version", version="%(prog)s 1.0.0")
    
    # 自定义数据文件
    parser.add_argument("--data-file", help="指定任务数据文件路径")
    
    # 子命令
    subparsers = parser.add_subparsers(dest="command", help="可用命令")
    
    # add 命令
    add_parser = subparsers.add_parser("add", help="添加新任务")
    add_parser.add_argument("title", help="任务标题")
    add_parser.add_argument("-d", "--description", help="任务描述", default="")
    add_parser.add_argument("-p", "--priority", 
                           choices=["high", "medium", "low"],
                           default="medium", help="任务优先级")
    
    # list 命令
    list_parser = subparsers.add_parser("list", help="列出任务")
    list_parser.add_argument("-a", "--all", action="store_true", 
                            help="显示包括已完成的所有任务")
    
    # complete 命令
    complete_parser = subparsers.add_parser("complete", help="完成任务")
    complete_parser.add_argument("task_id", type=int, help="任务ID")
    
    return parser

2.3 主函数入口

def main():
    """主函数 - 命令行入口点"""
    parser = create_parser()
    args = parser.parse_args()
    
    if not args.command:
        parser.print_help()
        sys.exit(1)
    
    # 创建任务管理器
    task_manager = TaskManager(args.data_file)
    
    try:
        if args.command == "add":
            task_manager.add_task(args.title, args.description, args.priority)
        elif args.command == "list":
            task_manager.list_tasks(show_completed=args.all)
        elif args.command == "complete":
            task_manager.complete_task(args.task_id)
        
    except KeyboardInterrupt:
        print("\n👋 再见!")
        sys.exit(0)
    except Exception as e:
        print(f"❌ 发生错误: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

第三步:配置文件编写

3.1 setup.py 配置

setup.py 是传统的包配置方式,仍被广泛使用:

#!/usr/bin/env python3
"""
setup.py - 用于 pip 安装的配置文件
"""

from setuptools import setup, find_packages
import os

def read_readme():
    with open("README.md", "r", encoding="utf-8") as f:
        return f.read()

setup(
    # 基本信息
    name="mytasks",
    version="1.0.0",
    description="🚀 简单易用的命令行任务管理工具",
    long_description=read_readme() if os.path.exists("README.md") else "",
    long_description_content_type="text/markdown",
    
    # 作者信息
    author="Your Name",
    author_email="your.email@example.com",
    url="https://github.com/yourname/mytasks",
    
    # 项目分类
    classifiers=[
        "Development Status :: 4 - Beta",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
        "Topic :: Utilities",
        "Environment :: Console",
    ],
    
    # 包配置
    py_modules=["demo"],  # 单文件模块
    python_requires=">=3.7",
    install_requires=[],  # 外部依赖
    
    # 开发依赖
    extras_require={
        "dev": [
            "pytest>=6.0",
            "black>=21.0",
            "flake8>=3.8",
        ],
    },
    
    # 🔑 关键配置:命令行入口点
    entry_points={
        "console_scripts": [
            "mytasks=demo:main",  # 命令名=模块:函数
            "task=demo:main",     # 提供简短别名
        ],
    },
    
    # 包含额外文件
    include_package_data=True,
    package_data={"": ["*.md", "*.txt"]},
)

关键说明:

  • entry_points 是最重要的配置,它定义了安装后可以在命令行使用的命令
  • "mytasks=demo:main" 表示:命令 mytasks 对应 demo.py 文件中的 main 函数
  • 可以定义多个别名,如 task 作为 mytasks 的简短版本

3.2 现代配置方式 (pyproject.toml)

pyproject.toml 是Python项目的现代标准配置格式:

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mytasks"
version = "1.0.0"
description = "🚀 简单易用的命令行任务管理工具"
readme = "README.md"
license = {text = "MIT"}
authors = [{name = "Your Name", email = "your.email@example.com"}]
requires-python = ">=3.7"
dependencies = []

[project.optional-dependencies]
dev = ["pytest>=6.0", "black>=21.0", "flake8>=3.8"]

[project.scripts]
mytasks = "demo:main"
task = "demo:main"

[project.urls]
Homepage = "https://github.com/yourname/mytasks"
Repository = "https://github.com/yourname/mytasks"

[tool.setuptools]
py-modules = ["demo"]

第四步:项目文档和许可证

4.1 README.md

# MyTasks - 命令行任务管理工具

🚀 一个简单易用的命令行任务管理工具

## 安装

```bash
pip install mytasks

使用方法

# 添加任务
mytasks add "学习Python" -d "完成教程" -p high

# 列出任务
mytasks list

# 完成任务
mytasks complete 1

# 查看帮助
mytasks --help

功能特性

  • ✅ 任务增删改查
  • 🎯 优先级管理
  • 📊 统计信息
  • 💾 本地存储

### 4.2 LICENSE

MIT License

Copyright (c) 2025 Your Name

Permission is hereby granted, free of charge, to any person obtaining a copy...


### 4.3 requirements.txt

生产依赖

本项目暂无外部依赖

开发依赖在 setup.py 的 extras_require 中定义


## 第五步:安装和测试

### 5.1 本地开发安装

```bash
# 进入项目目录
cd my_cli

# 开发模式安装(推荐)
pip install -e .

# 普通安装
pip install .

开发模式安装的优势:

  • 代码修改后无需重新安装
  • 便于调试和开发
  • 使用 -e 参数(editable)

重要提醒:包名冲突问题

在实际开发中,你可能会遇到包名冲突的问题:

# 卸载本地包后尝试从PyPI安装
$ pip uninstall mytasks
$ pip install mytasks
ERROR: Could not find a version that satisfies the requirement mytasks

这是因为:

  1. PyPI上没有该包:你的包只是本地开发,未发布到PyPI
  2. 包名被占用:常见名称可能已被其他开发者使用
  3. 名称规范:PyPI要求包名唯一且符合命名规范

解决方案:

# 1. 继续使用本地安装(开发阶段推荐)
pip install -e .

# 2. 或者使用构建的包文件安装
pip install dist/mytasks-1.0.0-py3-none-any.whl

# 3. 发布前检查包名是否可用
python -c "import requests; print('可用' if requests.get('https://pypi.org/simple/your-unique-name/').status_code == 404 else '已占用')"

5.2 验证安装

# 检查版本
mytasks --version

# 查看帮助
mytasks --help

# 测试功能
mytasks add "测试任务" -p high
mytasks list

5.3 不同安装方式对比

安装方式命令适用场景优缺点
开发模式pip install -e .本地开发✅ 修改后无需重装
❌ 依赖源码目录
标准安装pip install .本地测试✅ 独立安装
❌ 修改需重装
Wheel安装pip install dist/*.whl分发测试✅ 快速安装
❌ 需先构建
PyPI安装pip install mytasks生产使用✅ 简单方便
❌ 需发布到PyPI

实际演示对比:

# 场景1:开发阶段 - 推荐开发模式
cd my_cli
pip install -e .
# 修改代码后直接测试,无需重新安装

# 场景2:测试分发包
python -m build
pip install dist/mytasks-1.0.0-py3-none-any.whl
# 测试打包后的效果

# 场景3:卸载和重装
pip uninstall mytasks
pip install -e .  # 重新安装开发版本

# 场景4:检查安装状态
pip list | grep mytasks
pip show mytasks

5.4 功能测试

创建测试脚本 demo_installation.py

#!/usr/bin/env python3
"""安装和使用演示脚本"""

import subprocess
import sys

def run_command(cmd, description):
    """运行命令并显示结果"""
    print(f"\n🔧 {description}")
    print(f"💻 命令: {cmd}")
    print("-" * 50)
    
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        if result.stdout:
            print("📤 输出:")
            print(result.stdout)
        if result.returncode != 0:
            print(f"❌ 命令执行失败,返回码: {result.returncode}")
        else:
            print("✅ 命令执行成功")
    except Exception as e:
        print(f"❌ 执行异常: {e}")

def main():
    """演示完整流程"""
    print("🚀 MyTasks CLI 工具演示")
    
    # 安装
    run_command("pip install -e .", "安装工具")
    
    # 功能演示
    run_command('mytasks add "学习CLI开发" -p high', "添加任务")
    run_command("mytasks list", "列出任务")
    run_command("mytasks complete 1", "完成任务")

if __name__ == "__main__":
    main()

第六步:构建和分发

6.1 构建分发包

# 安装构建工具
pip install build

# 构建分发包
python -m build

# 或使用传统方式
python setup.py sdist bdist_wheel

这会在 dist/ 目录生成:

  • mytasks-1.0.0.tar.gz:源码包
  • mytasks-1.0.0-py3-none-any.whl:wheel包

6.2 本地测试安装

# 从wheel安装
pip install dist/mytasks-1.0.0-py3-none-any.whl

# 从源码包安装
pip install dist/mytasks-1.0.0.tar.gz

6.3 发布到PyPI

注意:发布前请确保包名唯一

# 1. 检查包名是否可用
pip search your-unique-package-name  # 已废弃,建议用浏览器访问 pypi.org

# 2. 或者用Python检查
python -c "import requests; r=requests.get('https://pypi.org/simple/your-unique-name/'); print('✅ 可用' if r.status_code==404 else '❌ 已存在')"

# 3. 安装上传工具
pip install twine

# 4. 检查包
twine check dist/*

# 5. 先上传到测试PyPI
twine upload --repository testpypi dist/*

# 6. 测试安装
pip install -i https://test.pypi.org/simple/ your-package-name

# 7. 确认无误后上传到正式PyPI
twine upload dist/*

PyPI发布最佳实践:

  1. 唯一包名:使用 your-name-toolname 格式避免冲突
  2. 语义化版本:遵循 主版本.次版本.修订版 格式
  3. 完整文档:包含详细的README和使用说明
  4. 测试覆盖:确保代码质量和功能完整性

第七步:实际运行演示

让我们看看实际运行效果:

# 安装工具
$ pip install -e .
Successfully installed mytasks-1.0.0

# 查看版本
$ mytasks --version
mytasks 1.0.0

# 添加任务
$ mytasks add "学习Python CLI开发" -d "完成命令行工具demo" -p high
✅ 任务已添加: 学习Python CLI开发

$ mytasks add "阅读技术文档" -p medium
✅ 任务已添加: 阅读技术文档

# 列出任务
$ mytasks list

📋 任务列表:
------------------------------------------------------------
⏳ [1] 🔴 学习Python CLI开发
    📝 完成命令行工具demo
    🕒 2025-08-02 23:48:49

⏳ [2] 🟡 阅读技术文档
    🕒 2025-08-02 23:48:58

# 完成任务
$ mytasks complete 1
✅ 任务已完成: 学习Python CLI开发

# 使用别名
$ task list
📋 任务列表:
------------------------------------------------------------
⏳ [2] 🟡 阅读技术文档
    🕒 2025-08-02 23:48:58

进阶特性

8.1 添加配置文件支持

# 支持配置文件
def load_config():
    config_file = os.path.expanduser("~/.mytasks_config.json")
    if os.path.exists(config_file):
        with open(config_file, 'r') as f:
            return json.load(f)
    return {"default_priority": "medium", "data_file": "~/.tasks.json"}

8.2 添加颜色输出

# 使用 colorama 库添加颜色
from colorama import Fore, Style, init

init(autoreset=True)

def print_success(message):
    print(f"{Fore.GREEN}{message}{Style.RESET_ALL}")

def print_error(message):
    print(f"{Fore.RED}{message}{Style.RESET_ALL}")

8.3 添加单元测试

# tests/test_demo.py
import pytest
from demo import TaskManager
import tempfile
import os

def test_add_task():
    with tempfile.NamedTemporaryFile(delete=False) as f:
        tm = TaskManager(f.name)
        tm.add_task("测试任务", "描述", "high")
        assert len(tm.tasks) == 1
        assert tm.tasks[0]["title"] == "测试任务"
        os.unlink(f.name)

常见问题和解决方案

9.1 包名冲突问题

问题现象:

$ pip uninstall mytasks
$ pip install mytasks
ERROR: Could not find a version that satisfies the requirement mytasks

原因分析:

  1. 本地包未发布:你的包只在本地,PyPI上不存在
  2. 包名冲突:常见名称可能被其他开发者占用
  3. 网络问题:无法访问PyPI服务器

解决方案:

# 方案1:继续使用本地安装
pip install -e .  # 开发模式
pip install .     # 标准安装

# 方案2:使用构建的wheel文件
python -m build
pip install dist/mytasks-1.0.0-py3-none-any.whl

# 方案3:更改包名(推荐用于发布)
# 修改 setup.py 中的 name="your-unique-name"

包名选择建议:

  • 使用个人或组织前缀:yourname-mytasks
  • 添加功能描述:cli-task-manager
  • 检查可用性:访问 https://pypi.org/project/your-package-name/

9.2 Unicode编码问题

在Windows下可能遇到Unicode显示问题:

# 解决方案:设置输出编码
if sys.platform == "win32":
    import codecs
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
    sys.stderr = codecs.getwriter("utf-8")(sys.stderr.detach())

9.3 路径问题

处理不同操作系统的路径差异:

# 使用 pathlib 处理路径
from pathlib import Path

data_file = Path.home() / ".tasks.json"

9.4 依赖管理

明确区分生产依赖和开发依赖:

# setup.py
install_requires=[
    "click>=8.0",  # 生产依赖
],
extras_require={
    "dev": [
        "pytest>=6.0",    # 开发依赖
        "black>=21.0",
    ],
}

9.5 命令行工具调试

常见问题:

  1. 命令未找到mytasks: command not found
  2. 权限错误Permission denied
  3. 模块导入失败ModuleNotFoundError

调试技巧:

# 检查安装状态
pip list | grep mytasks

# 查看安装位置
pip show mytasks

# 检查可执行文件
which mytasks  # Linux/Mac
where mytasks  # Windows

# 重新安装
pip uninstall mytasks
pip install -e . --force-reinstall

# 查看详细错误信息
python -c "from demo import main; main()" --help

最佳实践总结

10.1 代码结构

  1. 单一职责:每个函数只做一件事
  2. 清晰的接口:使用类型提示
  3. 错误处理:合理的异常处理和用户提示
  4. 可测试性:便于编写单元测试

10.2 用户体验

  1. 清晰的帮助信息:详细的命令说明和示例
  2. 友好的错误提示:告诉用户出了什么问题以及如何解决
  3. 一致的界面:统一的命令格式和输出风格
  4. 渐进式功能:从基础功能开始,逐步增加高级特性

10.3 项目管理

  1. 版本控制:使用语义化版本号
  2. 文档完善:README、注释、类型提示
  3. 自动化测试:单元测试、集成测试
  4. 持续改进:根据用户反馈优化功能

结语

通过本文的详细介绍,我们从零开始创建了一个完整的Python命令行工具,涵盖了:

  • 🏗️ 项目结构设计
  • 💻 核心代码实现
  • ⚙️ 配置文件编写
  • 📦 打包和安装
  • 🚀 分发和发布
  • 🧪 测试和调试
  • 🔧 问题排查和解决

开发流程总结

日常开发流程:

# 1. 初始设置
git clone your-project
cd your-project
pip install -e .

# 2. 开发循环
# 编辑代码 -> 测试功能 -> 调试问题
mytasks add "测试功能"
mytasks list

# 3. 版本发布
python -m build
pip install dist/*.whl  # 测试打包版本
git tag v1.0.1
twine upload dist/*     # 发布到PyPI

实际项目建议:

  1. 选择唯一包名:避免与PyPI现有包冲突
  2. 本地开发优先:使用 pip install -e . 进行开发
  3. 测试驱动开发:先写测试,再实现功能
  4. 渐进式发布:从本地 -> 测试PyPI -> 正式PyPI

关键技术点回顾

  1. entry_points 配置:将Python函数映射为命令行工具
  2. argparse 使用:创建专业的命令行界面
  3. 开发模式安装pip install -e . 提高开发效率
  4. 包名管理:避免冲突,选择合适的命名策略
  5. 错误处理:Unicode编码、路径处理、异常捕获

这个框架可以作为你未来开发命令行工具的模板。记住,好的命令行工具应该简单易用、功能完善、文档清晰。从小工具开始,逐步完善,最终可能成为被广泛使用的开源项目!

下一步建议:

  1. 尝试添加更多功能(搜索、标签、提醒等)
  2. 集成第三方服务(GitHub、Todoist等)
  3. 添加配置文件和主题支持
  4. 发布到PyPI供其他人使用

常见陷阱提醒:

  • 包名冲突:发布前检查PyPI包名可用性
  • 编码问题:Windows下注意Unicode显示
  • 权限问题:确保有足够权限安装和执行
  • 依赖管理:明确区分开发和生产依赖

希望这篇文章对你有帮助,开始你的命令行工具开发之旅吧!🚀

附录

A. 完整项目清单

确保你的项目包含以下文件:

my_cli/
├── demo.py              # ✅ 主程序
├── setup.py             # ✅ 安装配置(传统)
├── pyproject.toml       # ✅ 安装配置(现代)
├── README.md            # ✅ 项目说明
├── LICENSE              # ✅ 许可证
├── requirements.txt     # ✅ 依赖列表
├── demo_installation.py # ✅ 演示脚本
├── .gitignore          # 📁 Git忽略文件
├── tests/              # 📁 测试目录
│   └── test_demo.py    # 🧪 单元测试
├── dist/               # 📁 构建输出(自动生成)
└── build/              # 📁 构建缓存(自动生成)

B. 常用命令速查

# 开发环境设置
python -m venv venv
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate     # Windows
pip install -e .

# 代码质量检查
black demo.py             # 代码格式化
flake8 demo.py            # 代码检查
pytest tests/             # 运行测试

# 构建和分发
python -m build           # 构建包
twine check dist/*        # 检查包
twine upload --repository testpypi dist/*  # 测试发布

# 调试命令
pip list | grep mytasks   # 检查安装
pip show mytasks          # 查看详情
which mytasks            # 查找命令位置

C. .gitignore 模板

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
env.bak/
venv.bak/

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Project specific
.tasks.json

D. 进阶功能代码片段

配置文件支持:

import json
from pathlib import Path

def load_config():
    config_file = Path.home() / ".mytasks_config.json"
    default_config = {
        "default_priority": "medium",
        "data_file": str(Path.home() / ".tasks.json"),
        "theme": "default"
    }
    
    if config_file.exists():
        try:
            with open(config_file, 'r') as f:
                user_config = json.load(f)
                default_config.update(user_config)
        except (json.JSONDecodeError, IOError):
            pass
    
    return default_config

彩色输出:

try:
    from colorama import Fore, Style, init
    init(autoreset=True)
    COLORS_AVAILABLE = True
except ImportError:
    COLORS_AVAILABLE = False

def colored_print(message, color=None):
    if COLORS_AVAILABLE and color:
        colors = {
            'green': Fore.GREEN,
            'red': Fore.RED,
            'yellow': Fore.YELLOW,
            'blue': Fore.BLUE
        }
        print(f"{colors.get(color, '')}{message}{Style.RESET_ALL}")
    else:
        print(message)

日志记录:

import logging
from pathlib import Path

def setup_logging(verbose=False):
    log_level = logging.DEBUG if verbose else logging.INFO
    log_file = Path.home() / ".mytasks.log"
    
    logging.basicConfig(
        level=log_level,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler(log_file),
            logging.StreamHandler()
        ]
    )

E. 推荐资源


作者注: 本教程基于实际开发经验编写,所有代码都经过测试验证。如有问题或建议,欢迎反馈交流!