refactor(cli): 统一使用@px.task装饰器定义任务,重构任务注册和别名管理

1. 将folderzip/folderback/gittool中的旧TaskSpec定义替换为@px.task装饰器
2. 重构pymake模块,将maturin_build_cmd转为常量定义,合并别名配置
3. 精简测试文件中的冗余测试用例
This commit is contained in:
2026-06-28 18:12:30 +08:00
parent 5e561b4b3a
commit abc1152538
5 changed files with 56 additions and 142 deletions
+4 -13
View File
@@ -66,19 +66,10 @@ def backup_folder(src: str, dst: str, max_zip: int = 5) -> None:
zip_target(src_path, dst_path, max_zip) zip_target(src_path, dst_path, max_zip)
# ============================================================================ @px.task
# TaskSpec 定义 def folderback_default() -> None:
# ============================================================================ """备份当前目录到 ./backup."""
backup_folder(".", "./backup", 5)
folderback_default: px.TaskSpec = px.TaskSpec(
"folderback_default",
fn=lambda: backup_folder(".", "./backup", 5),
)
# ============================================================================
# CLI Runner
# ============================================================================
def main() -> None: def main() -> None:
+4 -10
View File
@@ -57,16 +57,10 @@ def zip_folders(cwd: str = ".") -> None:
archive_folder(dir_path) archive_folder(dir_path)
# ============================================================================ @px.task
# TaskSpec 定义 def folderzip_default() -> None:
# ============================================================================ """压缩当前目录下的所有文件夹."""
zip_folders(".")
folderzip_default: px.TaskSpec = px.TaskSpec("folderzip_default", fn=lambda: zip_folders("."))
# ============================================================================
# CLI Runner
# ============================================================================
def main() -> None: def main() -> None:
+10 -5
View File
@@ -46,7 +46,12 @@ def init_sub_dirs() -> None:
) )
isub: px.TaskSpec = px.TaskSpec("isub", fn=init_sub_dirs) @px.task(name="isub")
def isub() -> None:
"""初始化子目录的Git仓库."""
init_sub_dirs()
push: px.TaskSpec = px.TaskSpec("push", cmd=["git", "push"]) push: px.TaskSpec = px.TaskSpec("push", cmd=["git", "push"])
pull: px.TaskSpec = px.TaskSpec("pull", cmd=["git", "pull"]) pull: px.TaskSpec = px.TaskSpec("pull", cmd=["git", "pull"])
kill_tgit: px.TaskSpec = px.TaskSpec("task_kill", cmd=["taskkill", "/f", "/t", "/im", "tgitcache.exe"]) kill_tgit: px.TaskSpec = px.TaskSpec("task_kill", cmd=["taskkill", "/f", "/t", "/im", "tgitcache.exe"])
@@ -73,11 +78,11 @@ def main() -> None:
px.TaskSpec("add", cmd=["git", "add", "."], conditions=(lambda _: has_files(),)), px.TaskSpec("add", cmd=["git", "add", "."], conditions=(lambda _: has_files(),)),
px.TaskSpec("commit", cmd=["git", "commit", "-m", "chore: update"], depends_on=("add",)), px.TaskSpec("commit", cmd=["git", "commit", "-m", "chore: update"], depends_on=("add",)),
]), ]),
# 清理 # 清理chain: clean → status
"c": px.Graph.from_specs([ "c": px.Graph().chain(
px.TaskSpec("clean", cmd=["git", "clean", "-xfd", *EXCLUDE_CMDS]), px.TaskSpec("clean", cmd=["git", "clean", "-xfd", *EXCLUDE_CMDS]),
px.TaskSpec("status", cmd=["git", "status", "--porcelain"], depends_on=("clean",)), px.TaskSpec("status", cmd=["git", "status", "--porcelain"]),
]), ),
# 初始化、添加并提交 # 初始化、添加并提交
"i": px.Graph.from_specs([ "i": px.Graph.from_specs([
px.TaskSpec("init", cmd=["git", "init"], conditions=(lambda _: not_has_git_repo(),)), px.TaskSpec("init", cmd=["git", "init"], conditions=(lambda _: not_has_git_repo(),)),
+32 -50
View File
@@ -9,25 +9,14 @@ from __future__ import annotations
import pyflowx as px import pyflowx as px
from pyflowx.conditions import Constants from pyflowx.conditions import Constants
MATURIN_BUILD_COMMAND = ["maturin", "build", "-r"]
def maturin_build_cmd() -> list[str]:
"""获取 maturin 构建命令(根据平台自动添加参数).
Returns
-------
list[str]
完整的 maturin 构建命令列表.
"""
command = ["maturin", "build", "-r"].copy()
if Constants.IS_WINDOWS: if Constants.IS_WINDOWS:
command.extend(["--target", "x86_64-win7-windows-msvc", "-Zbuild-std", "-i", "python3.8"]) MATURIN_BUILD_COMMAND.extend(["--target", "x86_64-win7-windows-msvc", "-Zbuild-std", "-i", "python3.8"])
return command
# 扁平注册所有任务(px.cmd 自动从命令前两段推导 name) # 扁平注册所有任务(px.cmd 自动从命令前两段推导 name)
tasks: list[px.TaskSpec] = [ tasks: list[px.TaskSpec] = [
px.cmd(["uv", "build"]), px.cmd(["uv", "build"]),
px.cmd(maturin_build_cmd(), name="maturin_build"), px.cmd(MATURIN_BUILD_COMMAND),
px.cmd(["uv", "sync"]), px.cmd(["uv", "sync"]),
px.cmd(["gitt", "c"], name="git_clean"), px.cmd(["gitt", "c"], name="git_clean"),
px.cmd( px.cmd(
@@ -42,20 +31,40 @@ tasks: list[px.TaskSpec] = [
["pytest", "--cov", "-n", "8", "--dist", "loadfile", "--tb=short", "-v", "--color=yes", "--durations=10"], ["pytest", "--cov", "-n", "8", "--dist", "loadfile", "--tb=short", "-v", "--color=yes", "--durations=10"],
name="test_coverage", name="test_coverage",
), ),
px.cmd(["pyrefly", "check", "."], name="pyrefly_check"), px.cmd(["pyrefly", "check", "."]),
px.cmd(["git", "add", "-A"], name="git_add_all"), px.cmd(["git", "add", "-A"], name="git_add_all"),
px.cmd(["bumpversion"], name="bumpversion"), px.cmd(["bumpversion"]),
px.cmd(["bumpversion", "minor"], name="bumpversion_minor"), px.cmd(["bumpversion", "minor"]),
px.cmd(["git", "push"], name="git_push"), px.cmd(["git", "push"]),
px.cmd(["git", "push", "--tags"], name="git_push_tags"), px.cmd(["git", "push", "--tags"], name="git_push_tags"),
px.cmd(["hatch", "publish"], name="publish_python"), px.cmd(["hatch", "publish"], name="publish_python"),
px.cmd(["twine", "upload", "--disable-progress-bar"], name="twine_publish"), px.cmd(["twine", "upload", "--disable-progress-bar"], name="twine_publish"),
] ]
# 单任务别名(alias 名与任务名相同):直接 TaskSpec,避免 str 自引用 # 单任务别名(alias 名与任务名相同):直接内联 TaskSpec,避免 str 自引用
_doc = px.cmd(["sphinx-build", "-b", "html", "docs", "docs/_build"], name="doc") aliases: dict[str, str | list[str | px.TaskSpec] | px.TaskSpec | px.Graph] = {
_lint = px.cmd(["ruff", "check", "--fix", "--unsafe-fixes"], name="lint") # 构建命令
_tox = px.cmd(["tox", "-p", "auto"], name="tox") "b": "uv_build",
"bc": "maturin_build",
"ba": ["b", "bc"],
# 安装命令
"sync": "uv_sync",
# 清理命令
"c": "git_clean",
# 开发工具
"bump": ["c", "tc", "git_add_all", "bumpversion"],
"bumpmi": "bumpversion_minor",
"cov": ["git_clean", "test_coverage"],
"doc": px.cmd(["sphinx-build", "-b", "html", "docs", "docs/_build"], name="doc"),
"lint": px.cmd(["ruff", "check", "--fix", "--unsafe-fixes"], name="lint"),
"pb": ["twine_publish", "publish_python"],
"t": "test",
"tf": "test_fast",
"tc": ["pyrefly_check", "lint"],
"tox": px.cmd(["tox", "-p", "auto"], name="tox"),
# 发布命令
"p": ["git_clean", "git_push", "git_push_tags"],
}
def main() -> None: def main() -> None:
@@ -103,32 +112,5 @@ def main() -> None:
pymake lint # 格式化代码 pymake lint # 格式化代码
pymake type # 类型检查 pymake type # 类型检查
""" """
runner = px.CliRunner( runner = px.CliRunner(strategy="sequential", description="PyMake - Python 构建工具", tasks=tasks, aliases=aliases)
strategy="sequential",
description="PyMake - Python 构建工具",
tasks=tasks,
aliases={
# 构建命令
"b": "uv_build",
"bc": "maturin_build",
"ba": ["b", "bc"],
# 安装命令
"sync": "uv_sync",
# 清理命令
"c": "git_clean",
# 开发工具
"bump": ["c", "tc", "git_add_all", "bumpversion"],
"bumpmi": "bumpversion_minor",
"cov": ["git_clean", "test_coverage"],
"doc": _doc,
"lint": _lint,
"pb": ["twine_publish", "publish_python"],
"t": "test",
"tf": "test_fast",
"tc": ["pyrefly_check", "lint"],
"tox": _tox,
# 发布命令
"p": ["git_clean", "git_push", "git_push_tags"],
},
)
runner.run_cli() runner.run_cli()
+5 -63
View File
@@ -7,78 +7,20 @@ from unittest.mock import patch
import pytest import pytest
from pyflowx.cli import pymake from pyflowx.cli import pymake
from pyflowx.conditions import Constants
# ---------------------------------------------------------------------- #
# maturin_build_cmd
# ---------------------------------------------------------------------- #
class TestMaturinBuildCmd:
"""Test maturin_build_cmd function."""
def test_returns_list(self) -> None:
"""Should return a list."""
cmd = pymake.maturin_build_cmd()
assert isinstance(cmd, list)
def test_contains_maturin_build(self) -> None:
"""Should contain 'maturin' and 'build'."""
cmd = pymake.maturin_build_cmd()
assert "maturin" in cmd
assert "build" in cmd
def test_contains_release_flag(self) -> None:
"""Should contain release flag '-r'."""
cmd = pymake.maturin_build_cmd()
assert "-r" in cmd
def test_windows_includes_target(self) -> None:
"""On Windows, should include target-specific flags."""
cmd = pymake.maturin_build_cmd()
if Constants.IS_WINDOWS:
assert "--target" in cmd
assert "x86_64-win7-windows-msvc" in cmd
assert "-Zbuild-std" in cmd
assert "-i" in cmd
assert "python3.8" in cmd
else:
# On non-Windows, should not include Windows-specific flags
assert "--target" not in cmd
def test_does_not_mutate_on_multiple_calls(self) -> None:
"""Multiple calls should return independent lists."""
cmd1 = pymake.maturin_build_cmd()
cmd2 = pymake.maturin_build_cmd()
assert cmd1 == cmd2
# Mutating one should not affect the other
cmd1.append("extra")
assert "extra" not in cmd2
def test_non_windows_excludes_target_flags(self) -> None:
"""On non-Windows, should not include Windows-specific flags (覆盖 22->32 分支)."""
from unittest.mock import patch
with patch.object(pymake.Constants, "IS_WINDOWS", False):
cmd = pymake.maturin_build_cmd()
assert "maturin" in cmd
assert "build" in cmd
assert "-r" in cmd
assert "--target" not in cmd
assert "-Zbuild-std" not in cmd
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
# TaskSpec definitions # TaskSpec definitions
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
def _find_task(name: str) -> pymake.px.TaskSpec: def _find_task(name: str) -> pymake.px.TaskSpec:
"""从 pymake.tasks 或单任务别名变量中查找指定名称的 TaskSpec.""" """从 pymake.tasks 或 aliases 中查找指定名称的 TaskSpec."""
for spec in pymake.tasks: for spec in pymake.tasks:
if spec.name == name: if spec.name == name:
return spec return spec
# 单任务别名变量(_doc/_lint/_tox # 单任务别名doc/lint/tox内联在 aliases dict 中
alias_map = {"doc": pymake._doc, "lint": pymake._lint, "tox": pymake._tox} value = pymake.aliases.get(name)
if name in alias_map: if isinstance(value, pymake.px.TaskSpec):
return alias_map[name] return value
raise KeyError(f"任务 {name!r} 未找到") raise KeyError(f"任务 {name!r} 未找到")