refactor: restructure CLI with clear subcommand architecture
重构命令行接口,建立清晰的子命令架构,提升用户体验和代码可维护性。 主要变更: - 移除传统模式,统一使用子命令架构(check/convert/preview) - 将 preview 从 convert 的标志独立为子命令,职责分离 - 重命名参数:--no-check → --skip-validation - 新增 --force/-f:convert 命令支持强制覆盖已存在文件 - 新增 --host:preview 命令支持配置主机地址(局域网预览) - 新增 --no-browser:preview 命令支持不自动打开浏览器 - 优化 --port 默认值:从固定端口改为随机端口(30000-40000) 破坏性变更: - 不再支持传统模式(yaml2pptx.py input.yaml output.pptx) - convert 命令不再支持 --preview 参数,需使用 preview 子命令 文档更新: - 更新 README.md 和 README_DEV.md 的所有使用示例 - 更新命令行选项说明表格 - 新增 CLI 接口规范文档 OpenSpec: - 创建 cli-interface 规范(新能力) - 更新 browser-preview-server 规范(修改的能力) - 归档 refactor-cli-args change(45/45 任务完成)
This commit is contained in:
129
yaml2pptx.py
129
yaml2pptx.py
@@ -14,17 +14,20 @@ YAML to PPTX Converter
|
||||
将 YAML 格式的演示文稿源文件转换为 PPTX 文件
|
||||
|
||||
使用方法:
|
||||
# 子命令模式(推荐)
|
||||
uv run yaml2pptx.py convert input.yaml output.pptx
|
||||
# 验证 YAML 文件
|
||||
uv run yaml2pptx.py check input.yaml
|
||||
|
||||
# 传统模式(向后兼容)
|
||||
uv run yaml2pptx.py input.yaml output.pptx
|
||||
uv run yaml2pptx.py input.yaml # 自动生成 input.pptx
|
||||
# 转换为 PPTX
|
||||
uv run yaml2pptx.py convert input.yaml output.pptx
|
||||
uv run yaml2pptx.py convert input.yaml # 自动生成 input.pptx
|
||||
|
||||
# 启动预览服务器
|
||||
uv run yaml2pptx.py preview input.yaml
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
# 导入模块
|
||||
@@ -38,43 +41,33 @@ from validators import Validator
|
||||
|
||||
def parse_args():
|
||||
"""解析命令行参数"""
|
||||
# 检查是否使用了子命令
|
||||
if len(sys.argv) > 1 and sys.argv[1] in ['check', 'convert']:
|
||||
# 使用子命令解析
|
||||
parser = argparse.ArgumentParser(
|
||||
description="将 YAML 格式的演示文稿源文件转换为 PPTX 文件"
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest='command')
|
||||
parser = argparse.ArgumentParser(
|
||||
description="将 YAML 格式的演示文稿源文件转换为 PPTX 文件"
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest='command', required=True)
|
||||
|
||||
# check 子命令
|
||||
check_parser = subparsers.add_parser('check', help='验证 YAML 文件')
|
||||
check_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
|
||||
check_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
|
||||
# check 子命令
|
||||
check_parser = subparsers.add_parser('check', help='验证 YAML 文件')
|
||||
check_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
|
||||
check_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
|
||||
|
||||
# convert 子命令
|
||||
convert_parser = subparsers.add_parser('convert', help='转换 YAML 为 PPTX')
|
||||
convert_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
|
||||
convert_parser.add_argument("output", type=str, nargs="?", help="输出的 PPTX 文件路径(可选,默认为输入文件名.pptx)")
|
||||
convert_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
|
||||
convert_parser.add_argument("--preview", action="store_true", help="启动浏览器预览模式")
|
||||
convert_parser.add_argument("--port", type=int, default=None, help="预览服务器端口")
|
||||
convert_parser.add_argument("--no-check", action="store_true", help="跳过自动验证")
|
||||
# convert 子命令
|
||||
convert_parser = subparsers.add_parser('convert', help='转换 YAML 为 PPTX')
|
||||
convert_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
|
||||
convert_parser.add_argument("output", type=str, nargs="?", help="输出的 PPTX 文件路径(可选,默认为输入文件名.pptx)")
|
||||
convert_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
|
||||
convert_parser.add_argument("--skip-validation", action="store_true", help="跳过自动验证")
|
||||
convert_parser.add_argument("-f", "--force", action="store_true", help="强制覆盖已存在文件")
|
||||
|
||||
return parser.parse_args()
|
||||
else:
|
||||
# 使用传统解析(转换命令)- 向后兼容
|
||||
parser = argparse.ArgumentParser(
|
||||
description="将 YAML 格式的演示文稿源文件转换为 PPTX 文件"
|
||||
)
|
||||
parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
|
||||
parser.add_argument("output", type=str, nargs="?", help="输出的 PPTX 文件路径(可选,默认为输入文件名.pptx)")
|
||||
parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
|
||||
parser.add_argument("--preview", action="store_true", help="启动浏览器预览模式")
|
||||
parser.add_argument("--port", type=int, default=None, help="预览服务器端口")
|
||||
parser.add_argument("--no-check", action="store_true", help="跳过自动验证")
|
||||
args = parser.parse_args()
|
||||
args.command = None # 标记为非子命令(向后兼容模式)
|
||||
return args
|
||||
# preview 子命令
|
||||
preview_parser = subparsers.add_parser('preview', help='启动预览服务器')
|
||||
preview_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
|
||||
preview_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
|
||||
preview_parser.add_argument("--port", type=int, default=None, help="服务器端口(默认:随机端口 30000-40000)")
|
||||
preview_parser.add_argument("--host", type=str, default="127.0.0.1", help="主机地址(默认:127.0.0.1)")
|
||||
preview_parser.add_argument("--no-browser", action="store_true", help="不自动打开浏览器")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def handle_check(args):
|
||||
@@ -107,21 +100,44 @@ def handle_check(args):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def handle_preview(args):
|
||||
"""处理 preview 子命令"""
|
||||
# 检查输入文件是否存在
|
||||
input_path = Path(args.input)
|
||||
if not input_path.exists():
|
||||
log_error(f"输入文件不存在: {input_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# 处理端口:如果未指定,随机选择 30000-40000 范围内的端口
|
||||
port = args.port
|
||||
if port is None:
|
||||
port = random.randint(30000, 40000)
|
||||
|
||||
# 处理模板目录
|
||||
template_dir = Path(args.template_dir) if args.template_dir else None
|
||||
|
||||
# 启动预览服务器
|
||||
start_preview_server(
|
||||
yaml_file=input_path,
|
||||
template_dir=template_dir,
|
||||
port=port,
|
||||
host=args.host,
|
||||
open_browser=not args.no_browser
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数:加载 YAML → 渲染幻灯片 → 生成 PPTX"""
|
||||
try:
|
||||
args = parse_args()
|
||||
|
||||
# 处理 check 子命令
|
||||
# 路由到对应的处理函数
|
||||
if args.command == 'check':
|
||||
handle_check(args)
|
||||
return
|
||||
|
||||
# 处理 convert 子命令或传统模式(向后兼容)
|
||||
# convert 子命令和无子命令的行为相同
|
||||
if args.command == 'convert' or args.command is None:
|
||||
elif args.command == 'convert':
|
||||
handle_convert(args)
|
||||
return
|
||||
elif args.command == 'preview':
|
||||
handle_preview(args)
|
||||
|
||||
except YAMLError as e:
|
||||
log_error(str(e))
|
||||
@@ -134,7 +150,7 @@ def main():
|
||||
|
||||
|
||||
def handle_convert(args):
|
||||
"""处理 convert 子命令或传统转换模式"""
|
||||
"""处理 convert 子命令"""
|
||||
# 检查是否提供了输入文件
|
||||
if not args.input:
|
||||
log_error("错误: 缺少输入文件参数")
|
||||
@@ -143,12 +159,11 @@ def handle_convert(args):
|
||||
# 处理输入文件路径
|
||||
input_path = Path(args.input)
|
||||
|
||||
# 预览模式
|
||||
if args.preview:
|
||||
start_preview_server(input_path, args.template_dir, args.port)
|
||||
return
|
||||
# 检查输入文件是否存在
|
||||
if not input_path.exists():
|
||||
log_error(f"输入文件不存在: {input_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# PPTX 生成模式
|
||||
# 处理输出文件路径
|
||||
if args.output:
|
||||
output_path = Path(args.output)
|
||||
@@ -156,11 +171,17 @@ def handle_convert(args):
|
||||
# 自动生成输出文件名
|
||||
output_path = input_path.with_suffix(".pptx")
|
||||
|
||||
# 检查输出文件是否已存在(除非使用 --force)
|
||||
if output_path.exists() and not args.force:
|
||||
log_error(f"输出文件已存在: {output_path}")
|
||||
log_error("使用 --force 或 -f 强制覆盖")
|
||||
sys.exit(1)
|
||||
|
||||
log_info(f"开始转换: {input_path}")
|
||||
log_info(f"输出文件: {output_path}")
|
||||
|
||||
# 自动验证(除非使用 --no-check)
|
||||
if not args.no_check:
|
||||
# 自动验证(除非使用 --skip-validation)
|
||||
if not args.skip_validation:
|
||||
log_info("验证 YAML 文件...")
|
||||
template_dir = Path(args.template_dir) if args.template_dir else None
|
||||
validator = Validator()
|
||||
|
||||
Reference in New Issue
Block a user