版本编辑器 V2 示例
本示例展示了如何使用新版本的编辑器(VersionEditorV2)来编辑 requirements.txt 文件。
为什么使用 V2?
VersionEditorV2 相比旧版本有以下优势:
- ✅ 基于 AST 编辑 - 更可靠,不会破坏格式
- ✅ 批量操作性能 - 6.1倍性能提升
- ✅ 内存效率 - 节省 77% 内存使用
- ✅ 功能完整 - 支持添加、删除、查询等操作
完整示例代码
go
package main
import (
"fmt"
"log"
"github.com/scagogogo/python-requirements-parser/pkg/editor"
)
func main() {
fmt.Println("=== 基于Parser的版本编辑器示例 ===")
fmt.Println()
// 创建新版本编辑器
editorV2 := editor.NewVersionEditorV2()
// 示例requirements.txt内容
content := `# Production dependencies
Django>=3.2.0,<4.0.0 # Web framework
psycopg2-binary==2.9.1 # PostgreSQL adapter
redis>=3.5.0 # Cache backend
celery[redis]>=5.1.0 # Task queue
gunicorn>=20.1.0 # WSGI server
# Development dependencies
pytest>=6.2.0 # Testing framework
pytest-django>=4.4.0 # Django integration for pytest
black==21.9b0 # Code formatter
flake8>=3.9.0 # Linting
mypy>=0.910 # Type checking
# Complex dependencies
-e git+https://github.com/user/custom-package.git@v1.0.0#egg=custom-package
https://example.com/special-package.whl
./local-development-package
# Optional dependencies
sentry-sdk[django]>=1.4.0; extra == "monitoring" # Error tracking
django-debug-toolbar>=3.2.0; extra == "debug" # Debug toolbar`
fmt.Println("原始requirements.txt内容:")
fmt.Println(content)
fmt.Println()
// 1. 解析requirements文件
fmt.Println("=== 1. 解析requirements文件 ===")
doc, err := editorV2.ParseRequirementsFile(content)
if err != nil {
log.Fatalf("解析失败: %v", err)
}
// 列出所有包
packages := editorV2.ListPackages(doc)
fmt.Printf("发现 %d 个包依赖:\n", len(packages))
for _, pkg := range packages {
fmt.Printf(" - %s %s", pkg.Name, pkg.Version)
if len(pkg.Extras) > 0 {
fmt.Printf(" [%s]", pkg.Extras)
}
if pkg.Markers != "" {
fmt.Printf(" ; %s", pkg.Markers)
}
if pkg.Comment != "" {
fmt.Printf(" # %s", pkg.Comment)
}
fmt.Println()
}
fmt.Println()
// 2. 单个包版本更新
fmt.Println("=== 2. 单个包版本更新 ===")
err = editorV2.UpdatePackageVersion(doc, "Django", ">=3.2.13,<4.0.0")
if err != nil {
log.Fatalf("更新Django版本失败: %v", err)
}
fmt.Println("✅ Django版本已更新为安全版本")
err = editorV2.UpdatePackageVersion(doc, "black", "==22.3.0")
if err != nil {
log.Fatalf("更新black版本失败: %v", err)
}
fmt.Println("✅ black版本已更新")
fmt.Println()
// 3. 批量版本更新
fmt.Println("=== 3. 批量版本更新 ===")
securityUpdates := map[string]string{
"psycopg2-binary": "==2.9.3", // 安全更新
"redis": ">=4.0.0", // 主要版本升级
"pytest": ">=7.0.0", // 主要版本升级
"mypy": ">=0.950", // 新版本
}
err = editorV2.BatchUpdateVersions(doc, securityUpdates)
if err != nil {
log.Printf("批量更新警告: %v", err)
} else {
fmt.Println("✅ 批量安全更新完成")
}
fmt.Println()
// 4. 添加新包
fmt.Println("=== 4. 添加新包 ===")
err = editorV2.AddPackage(doc, "fastapi", ">=0.95.0", []string{"all"}, `python_version >= "3.7"`)
if err != nil {
log.Fatalf("添加fastapi失败: %v", err)
}
fmt.Println("✅ 添加了新包: fastapi[all]>=0.95.0")
err = editorV2.AddPackage(doc, "uvicorn", ">=0.18.0", []string{"standard"}, "")
if err != nil {
log.Fatalf("添加uvicorn失败: %v", err)
}
fmt.Println("✅ 添加了新包: uvicorn[standard]>=0.18.0")
fmt.Println()
// 5. 更新包的extras
fmt.Println("=== 5. 更新包的extras ===")
err = editorV2.UpdatePackageExtras(doc, "celery", []string{"redis", "auth"})
if err != nil {
log.Fatalf("更新celery extras失败: %v", err)
}
fmt.Println("✅ 更新了celery的extras")
fmt.Println()
// 6. 获取包信息
fmt.Println("=== 6. 获取包信息 ===")
djangoInfo, err := editorV2.GetPackageInfo(doc, "Django")
if err != nil {
log.Fatalf("获取Django信息失败: %v", err)
}
fmt.Printf("Django包信息:\n")
fmt.Printf(" 名称: %s\n", djangoInfo.Name)
fmt.Printf(" 版本: %s\n", djangoInfo.Version)
fmt.Printf(" 注释: %s\n", djangoInfo.Comment)
fmt.Println()
// 7. 移除包
fmt.Println("=== 7. 移除包 ===")
err = editorV2.RemovePackage(doc, "flake8")
if err != nil {
log.Fatalf("移除flake8失败: %v", err)
}
fmt.Println("✅ 移除了flake8包")
fmt.Println()
// 8. 序列化结果
fmt.Println("=== 8. 最终结果 ===")
finalResult := editorV2.SerializeToString(doc)
fmt.Println("更新后的requirements.txt内容:")
fmt.Println(finalResult)
fmt.Println()
// 9. 展示新版本编辑器的优势
fmt.Println("=== 新版本编辑器的优势 ===")
fmt.Println("✅ 基于AST的编辑,更可靠")
fmt.Println("✅ 完美保留注释、空行、格式")
fmt.Println("✅ 支持复杂格式(VCS、URL、本地路径)")
fmt.Println("✅ 提供丰富的编辑操作(添加、删除、批量更新)")
fmt.Println("✅ 更好的错误处理和验证")
fmt.Println("✅ 批量操作性能优异(6倍性能提升)")
fmt.Println("✅ 支持包信息查询和列表操作")
fmt.Println("✅ 类型安全的API设计")
// 10. 性能对比示例
fmt.Println()
fmt.Println("=== 性能对比 ===")
fmt.Println("批量更新5个包的性能对比:")
fmt.Println(" 旧版本编辑器: ~601μs (需要5次解析)")
fmt.Println(" 新版本编辑器: ~98μs (只需1次解析)")
fmt.Println(" 性能提升: 6.1倍")
fmt.Println()
fmt.Println("内存使用对比:")
fmt.Println(" 旧版本编辑器: 357KB (重复解析)")
fmt.Println(" 新版本编辑器: 83KB (单次解析)")
fmt.Println(" 内存节省: 77%")
}
核心功能演示
1. 文档解析和序列化
go
// 解析
doc, err := editorV2.ParseRequirementsFile(content)
// 序列化
result := editorV2.SerializeToString(doc)
2. 包版本管理
go
// 单个更新
err = editorV2.UpdatePackageVersion(doc, "Django", ">=3.2.13")
// 批量更新
updates := map[string]string{
"psycopg2-binary": "==2.9.3",
"redis": ">=4.0.0",
"pytest": ">=7.0.0",
}
err = editorV2.BatchUpdateVersions(doc, updates)
3. 包管理操作
go
// 添加包
err = editorV2.AddPackage(doc, "fastapi", ">=0.95.0",
[]string{"all"}, `python_version >= "3.7"`)
// 移除包
err = editorV2.RemovePackage(doc, "old-package")
// 更新extras
err = editorV2.UpdatePackageExtras(doc, "celery", []string{"redis", "auth"})
4. 包信息查询
go
// 获取包信息
info, err := editorV2.GetPackageInfo(doc, "Django")
// 列出所有包
packages := editorV2.ListPackages(doc)
性能优势
批量操作对比
操作 | 旧版本编辑器 | 新版本编辑器V2 | 性能提升 |
---|---|---|---|
5个包批量更新 | 601μs | 98μs | 6.1倍 |
内存使用 | 357KB | 83KB | 77%节省 |
分配次数 | 4893次 | 1355次 | 72%减少 |
为什么更快?
- 旧版本: 每次更新都需要重新解析整个文件
- 新版本: 只解析一次,在内存中的AST上操作
格式保留
新版本编辑器完美保留:
- ✅ 所有注释(行注释和行尾注释)
- ✅ 空行和分组结构
- ✅ Extras 和环境标记
- ✅ 复杂格式(VCS、URL、本地路径)
- ✅ 全局选项和包选项
错误处理
go
// 包不存在
err = editorV2.UpdatePackageVersion(doc, "nonexistent", "==1.0.0")
// 返回: "在requirements中未找到包: nonexistent"
// 无效版本格式
err = editorV2.UpdatePackageVersion(doc, "flask", "invalid_version")
// 返回: "无效的版本约束格式: invalid_version"
// 添加已存在的包
err = editorV2.AddPackage(doc, "existing-package", ">=1.0.0", nil, "")
// 返回: "包 existing-package 已存在"
最佳实践
1. 使用批量操作
go
// ❌ 不推荐:多次单独更新
for pkg, version := range updates {
err := editorV2.UpdatePackageVersion(doc, pkg, version)
}
// ✅ 推荐:批量更新
err := editorV2.BatchUpdateVersions(doc, updates)
2. 重用编辑器实例
go
// ✅ 推荐:重用编辑器
editorV2 := editor.NewVersionEditorV2()
// 处理多个文件
for _, content := range contents {
doc, err := editorV2.ParseRequirementsFile(content)
// 编辑操作...
}
3. 错误处理
go
err := editorV2.BatchUpdateVersions(doc, updates)
if err != nil {
// 批量操作可能部分成功,检查具体错误
log.Printf("批量更新警告: %v", err)
}