Skip to content

版本编辑器 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μs98μs6.1倍
内存使用357KB83KB77%节省
分配次数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)
}

下一步

相关链接

Released under the MIT License