一、项目背景

本次软件开源课程中,参与 OpenHUTB/nn 仓库的维护与改进工作。软件开源的意义在于促进共享、推动协作、提升质量、减少重复开发,并帮助开发者在真实项目中成长。

二、模块介绍:什么是 setup_tool

setup_tool 模块本质上是一个 命令行版 setup 流程演示工具。它并不真正执行复杂安装,而是模拟 setup 过程中的多个步骤,并把整个流程以“进度条、步骤编号、状态输出、最终汇总”的形式展示出来。

从功能上看,它主要完成以下任务:

  • 显示当前正在执行的步骤
  • 输出整体进度百分比和当前步骤编号
  • 估算已经消耗的时间和剩余时间
  • 对每一步输出状态结果,例如 OKSKIPERROR
  • 在流程结束后给出总结信息,例如总步骤数、总耗时、跳过数、下载数和错误数

从代码结构上看,模块主要由两部分组成。

第一部分是 ProgressDemo 类。
这个类负责处理与显示相关的逻辑,包括进度条显示、状态计数、时间统计和最终汇总输出。

第二部分是 main() 函数。
main() 负责组织演示流程,也就是决定要执行哪些步骤、每个步骤包含哪些动作、以及整个脚本以什么模式和速度运行。

可以把它理解成一个“小型命令行流程演示器”。

三、原始版本存在的问题

在开始改进之前,setup_tool 模块虽然能运行,但从工程化角度看,主要存在以下几个问题。

1. 主流程中存在大量重复代码

原始版本的 main() 函数中,每一步几乎都是手写一段类似逻辑:

  • 调用 show_progress()
  • sleep 一段时间
  • 输出一步的结果

这种写法直观,但重复度很高。一旦步骤变多,代码会迅速膨胀,后续如果需要增删步骤,就要修改很多重复代码。

2. 演示速度是写死的

原始版本中每一步的等待时间都直接写在代码里,例如 time.sleep(1)time.sleep(2)。这样导致脚本只能以一种固定节奏运行。如果想快速展示,就必须手工改代码;如果想慢速观察,也同样需要改代码。

3. 演示流程只有一种模式

原始脚本默认总是执行完整流程。对于完整功能展示来说没问题,但如果只是课堂汇报或者快速演示核心步骤,这种“只能完整运行”的方式就不够灵活。

4. 缺少基础测试保障

虽然脚本能运行,但像状态计数这样的核心行为,原来缺少最基本的验证。例如跳过数、下载数、错误数这些计数逻辑,最好通过测试确认其行为正确。

四、改进思路

setup_tool 的改进不是一次性大改,而是采用了逐步递进的方式。

整体思路是:

  1. 先整理结构,让重复逻辑收敛
  2. 再增加功能,让脚本更灵活
  3. 最后补充测试,让关键行为有验证依据

这条路线的核心不是盲目加功能,而是先把结构打理清楚,再在清晰结构上做增强。

五、具体改进内容

5.1 结构优化:把重复的步骤执行逻辑改成“步骤列表 + 循环执行”

这是整个改进中最关键的一步。

修改前

原始版本中的步骤执行是逐段硬编码的,例如:

# Step 1
demo.show_progress("Checking hutb_downloader.exe")
time.sleep(1)
demo.step_result("skip", "hutb_downloader.exe already exists")

# Step 2
demo.show_progress("Checking dependencies directory")
time.sleep(1)
demo.step_result("skip", "dependencies directory already exists")

这种写法会在 main() 中反复出现。

修改后

把这些步骤整理成统一的数据结构,用 steps 列表描述步骤,再通过循环统一执行:

steps = [
    {
        "description": "Checking hutb_downloader.exe",
        "actions": [
            {"type": "sleep", "seconds": 1},
            {"type": "result", "status": "skip", "message": "hutb_downloader.exe already exists"},
        ],
    },
    {
        "description": "Checking dependencies directory",
        "actions": [
            {"type": "sleep", "seconds": 1},
            {"type": "result", "status": "skip", "message": "dependencies directory already exists"},
        ],
    },
]

demo = ProgressDemo(total_steps=len(steps))

for step in steps:
    demo.show_progress(step["description"])

    for action in step["actions"]:
        if action["type"] == "sleep":
            time.sleep(action["seconds"])
        elif action["type"] == "print":
            print(action["message"])
        elif action["type"] == "result":
            demo.step_result(action["status"], action["message"])

这样,原本写死在 main() 里的重复逻辑就被统一抽象成了“步骤数据 + 循环执行”。

运行方式

python src/setup_tool/main.py

改进效果

这一改动带来了几个直接收益:

  • 减少重复代码
  • 使主流程结构更清晰
  • 后续如果增加或删除步骤,只需要改步骤数据
  • 为后续继续增加参数和模式提供了基础

这一步可以理解为:
把固定写死的流程,改造成了数据驱动的流程。

5.2 功能增强:增加演示速度配置

在完成结构整理之后,我继续为脚本增加了速度参数,使演示节奏可以通过命令行选择。

修改前

原来的 sleep 执行方式是固定的:

if action["type"] == "sleep":
    time.sleep(action["seconds"])

也就是说,运行速度完全写死。

修改后

我引入了 argparse,增加了 --speed 参数,并定义了速度倍率:

import argparse
def parse_args():
    parser = argparse.ArgumentParser(
        description="Setup tool progress display demo"
    )
    parser.add_argument(
        "--speed",
        choices=["fast", "normal", "slow"],
        default="normal",
        help="Set demo speed: fast, normal, or slow",
    )
    return parser.parse_args()
args = parse_args()

speed_map = {
    "fast": 0.3,
    "normal": 1.0,
    "slow": 1.5,
}
speed_factor = speed_map[args.speed]
if action["type"] == "sleep":
    time.sleep(action["seconds"] * speed_factor)

并且在运行标题中增加了显示:

print(f"  Demo speed: {args.speed}")

运行方式

python src/setup_tool/main.py --speed fast

python src/setup_tool/main.py --speed alow

改进效果

这一改动让脚本从固定速度,变成了可根据场景调节速度的工具。

5.3 测试补充:增加基础行为测试

为了让前面的结构优化和功能增强更可靠,补充了针对 ProgressDemo 的基础测试。

测试内容

新增了一个测试文件,用于验证:

  • 初始化状态是否正确
  • step_result("skip", ...) 是否会增加 skipped
  • step_result("download", ...) 是否会增加 downloaded
  • step_result("error", ...) 是否会增加 errors

测试运行结果为 4 个测试全部通过。

改进效果

这一改动的意义在于:

  • 让模块的核心行为不再只是“看起来对”,而是“有测试验证”
  • 为后续继续修改这个模块提供安全保障
  • 体现了较完整的软件工程流程:
    结构调整 → 功能增强 → 测试验证

5.4 功能增强:增加演示模式配置

这是最后一步,也是最适合课程结课汇报展示的一步。

修改前

原始脚本只能执行一套完整步骤,所有演示场景都必须跑完整流程。

修改后

在命令行参数中增加了 --mode

parser.add_argument(
    "--mode",
    choices=["basic", "full"],
    default="full",
    help="Set demo mode: basic or full",
)

然后把原有完整步骤集合命名为 full_steps,再从中提取核心步骤组成 basic_steps

basic_steps = [
    full_steps[0],
    full_steps[2],
    full_steps[6],
    full_steps[8],
    full_steps[10],
]

接着根据模式选择执行步骤:

if args.mode == "basic":
    steps = basic_steps
else:
    steps = full_steps

demo = ProgressDemo(total_steps=len(steps))

并在标题区增加了当前模式显示:

print(f"  Demo mode: {args.mode}")

运行方式

python src/setup_tool/main.py --mode full
python src/setup_tool/main.py --mode basic

改进效果

加入模式配置之后,脚本可以根据场景切换不同的流程集合:

  • full:完整演示,适合完整展示模块能力
  • basic:精简演示,适合课堂快速汇报

速度参数和模式参数可以组合使用,例如:

python src/setup_tool/main.py --mode basic --speed fast
python src/setup_tool/main.py --mode full --speed slow

这意味着 setup_tool 不再只是一个固定脚本,而是真正具备了“按不同场景选择流程”的能力。

六、最终效果总结

经过这些连续改进,setup_tool 模块完成了从“固定 demo 脚本”到“灵活命令行工具”的提升。

改进前

  • 步骤逻辑写死,重复代码较多
  • 演示速度固定
  • 演示模式单一
  • 缺少针对核心行为的测试

改进后

  • 步骤执行逻辑结构化
  • 支持速度配置 --speed
  • 支持模式配置 --mode
  • 核心行为具备基础测试支持

如果用一句话概括最终成果,那就是:

把一个固定流程、固定速度、固定模式的演示脚本,逐步改造成了一个结构更清晰、速度可调、模式可选、具备基础测试的小型命令行进度演示工具。

七、这部分工作的价值

1. 对代码本身的价值

setup_tool 模块的结构更清晰、重复更少、扩展更方便,代码维护成本下降了。

2. 对功能演示的价值

脚本可以根据不同场景选择不同的速度和模式,更适合课堂汇报和展示。

3. 对工程实践的价值

这部分工作体现了一条完整的软件工程改进主线:

  • 先理解模块职责
  • 再整理主流程结构
  • 再做功能参数化
  • 再做模式配置化
  • 最后补上基础测试

这比单纯“修几行代码”更能体现工程思维。

八、结论

在这次课程实践中,我最终将重点放在 setup_tool 模块,并围绕它完成了连续的结构优化、功能增强和测试补充工作。

这个模块原本只是一个固定流程的 setup 演示脚本,而经过改进之后,它已经具备了:

  • 更清晰的步骤组织方式
  • 更灵活的速度调节能力
  • 更灵活的模式切换能力
  • 更基本的行为测试保障