Editors API
The editor package provides powerful tools for editing and manipulating Python requirements.txt files.
Overview
The editor package offers three different editors, each optimized for different use cases:
- PositionAwareEditor - Minimal diff editing ⭐ (Recommended)
- VersionEditorV2 - Full reconstruction editing
- VersionEditor - Basic text editing
import "github.com/scagogogo/python-requirements-parser/pkg/editor"Editor Comparison
| Feature | PositionAwareEditor | VersionEditorV2 | VersionEditor |
|---|---|---|---|
| Minimal Diff | ✅ Best | ❌ No | ❌ No |
| Format Preservation | ✅ Perfect | ✅ Good | ⚠️ Basic |
| Performance | ✅ Fastest | ✅ Fast | ⚠️ Slower |
| Memory Usage | ✅ Lowest | ✅ Low | ⚠️ Higher |
| Complex Editing | ⚠️ Limited | ✅ Full | ✅ Full |
| Use Case | Production updates | Development tools | Simple scripts |
PositionAwareEditor
The PositionAwareEditor is the recommended editor for production environments where minimal changes are crucial.
Key Features
- Minimal diff editing - Only changes what's necessary
- Perfect format preservation - Maintains comments, spacing, and structure
- High performance - Nanosecond-level update operations
- Zero allocations - Batch updates with no memory allocations
Constructor
func NewPositionAwareEditor() *PositionAwareEditorExample:
editor := editor.NewPositionAwareEditor()Core Methods
ParseRequirementsFile()
Parses a requirements file and creates a position-aware document.
func (e *PositionAwareEditor) ParseRequirementsFile(content string) (*PositionAwareDocument, error)Parameters:
content(string): Requirements file content
Returns:
*PositionAwareDocument: Document with position informationerror: Parse error if any
Example:
content := `flask==1.0.0 # Web framework
django>=3.2.0 # Another framework`
doc, err := editor.ParseRequirementsFile(content)
if err != nil {
log.Fatal(err)
}UpdatePackageVersion()
Updates a single package version with minimal changes.
func (e *PositionAwareEditor) UpdatePackageVersion(doc *PositionAwareDocument, packageName, newVersion string) errorParameters:
doc(*PositionAwareDocument): Document to updatepackageName(string): Name of package to updatenewVersion(string): New version constraint
Returns:
error: Error if package not found or invalid version
Example:
err := editor.UpdatePackageVersion(doc, "flask", "==2.0.1")
if err != nil {
log.Fatal(err)
}BatchUpdateVersions()
Updates multiple packages in a single operation.
func (e *PositionAwareEditor) BatchUpdateVersions(doc *PositionAwareDocument, updates map[string]string) errorParameters:
doc(*PositionAwareDocument): Document to updateupdates(map[string]string): Map of package names to new versions
Returns:
error: Error if any updates fail
Example:
updates := map[string]string{
"flask": "==2.0.1",
"django": ">=3.2.13",
"requests": ">=2.28.0",
}
err := editor.BatchUpdateVersions(doc, updates)
if err != nil {
log.Printf("Some updates failed: %v", err)
}SerializeToString()
Serializes the document back to string format with minimal changes.
func (e *PositionAwareEditor) SerializeToString(doc *PositionAwareDocument) stringParameters:
doc(*PositionAwareDocument): Document to serialize
Returns:
string: Updated requirements content
Example:
result := editor.SerializeToString(doc)
fmt.Println(result)GetPackageInfo()
Retrieves information about a specific package.
func (e *PositionAwareEditor) GetPackageInfo(doc *PositionAwareDocument, packageName string) (*models.Requirement, error)Example:
info, err := editor.GetPackageInfo(doc, "flask")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Flask version: %s\n", info.Version)ListPackages()
Lists all packages in the document.
func (e *PositionAwareEditor) ListPackages(doc *PositionAwareDocument) []*models.RequirementExample:
packages := editor.ListPackages(doc)
fmt.Printf("Found %d packages\n", len(packages))Performance Characteristics
| Operation | Time | Memory | Allocations |
|---|---|---|---|
| Single update | 67.67 ns | 8 B | 1 alloc |
| Batch update (10 packages) | 374.1 ns | 0 B | 0 allocs |
| Serialize (100 packages) | 4.3 µs | 8.2 KB | 102 allocs |
Real-World Example
editor := editor.NewPositionAwareEditor()
// Original content with complex formatting
content := `# Production dependencies
flask==1.0.0 # Web framework
django[rest,auth]>=3.2.0,<4.0.0 # Web framework with extras
# VCS dependencies (preserved)
git+https://github.com/user/project.git#egg=project
# Environment markers (preserved)
pywin32>=1.0; platform_system == "Windows"`
doc, err := editor.ParseRequirementsFile(content)
if err != nil {
log.Fatal(err)
}
// Security updates
updates := map[string]string{
"flask": "==2.0.1",
"django": ">=3.2.13,<4.0.0",
}
err = editor.BatchUpdateVersions(doc, updates)
if err != nil {
log.Fatal(err)
}
result := editor.SerializeToString(doc)
// Only 2 lines changed, all formatting preservedVersionEditorV2
The VersionEditorV2 provides comprehensive editing capabilities with full parser support.
Key Features
- Full editing capabilities - Add, remove, update packages
- Parser-based - Understands all pip formats
- Good performance - Optimized for development workflows
- Comprehensive API - Supports extras, markers, and all package types
Constructor
func NewVersionEditorV2() *VersionEditorV2Core Methods
ParseRequirementsFile()
func (v *VersionEditorV2) ParseRequirementsFile(content string) (*RequirementsDocument, error)UpdatePackageVersion()
func (v *VersionEditorV2) UpdatePackageVersion(doc *RequirementsDocument, packageName, version string) errorAddPackage()
Adds a new package with full specification.
func (v *VersionEditorV2) AddPackage(doc *RequirementsDocument, packageName, version string, extras []string, markers string) errorExample:
err := editor.AddPackage(doc, "fastapi", ">=0.95.0",
[]string{"all"}, `python_version >= "3.7"`)RemovePackage()
Removes a package from the requirements.
func (v *VersionEditorV2) RemovePackage(doc *RequirementsDocument, packageName string) errorExample:
err := editor.RemovePackage(doc, "old-package")UpdatePackageExtras()
Updates the extras for a package.
func (v *VersionEditorV2) UpdatePackageExtras(doc *RequirementsDocument, packageName string, extras []string) errorExample:
err := editor.UpdatePackageExtras(doc, "django", []string{"rest", "auth"})UpdatePackageMarkers()
Updates the environment markers for a package.
func (v *VersionEditorV2) UpdatePackageMarkers(doc *RequirementsDocument, packageName, markers string) errorExample:
err := editor.UpdatePackageMarkers(doc, "pywin32", `platform_system == "Windows"`)Full Example
editor := editor.NewVersionEditorV2()
content := `flask==1.0.0
django>=3.2.0`
doc, err := editor.ParseRequirementsFile(content)
if err != nil {
log.Fatal(err)
}
// Add new package with extras and markers
err = editor.AddPackage(doc, "fastapi", ">=0.95.0",
[]string{"all"}, `python_version >= "3.7"`)
if err != nil {
log.Fatal(err)
}
// Update existing package
err = editor.UpdatePackageVersion(doc, "flask", "==2.0.1")
if err != nil {
log.Fatal(err)
}
// Add extras to existing package
err = editor.UpdatePackageExtras(doc, "django", []string{"rest", "auth"})
if err != nil {
log.Fatal(err)
}
result := editor.SerializeToString(doc)
fmt.Println(result)VersionEditor
The VersionEditor provides basic text-based editing capabilities.
Key Features
- Simple API - Easy to use for basic operations
- Text-based - Works directly with version strings
- Lightweight - Minimal dependencies
Constructor
func NewVersionEditor() *VersionEditorCore Methods
SetExactVersion()
Sets an exact version constraint.
func (v *VersionEditor) SetExactVersion(req *models.Requirement, version string) (*models.Requirement, error)SetMinimumVersion()
Sets a minimum version constraint.
func (v *VersionEditor) SetMinimumVersion(req *models.Requirement, version string) (*models.Requirement, error)SetCompatibleVersion()
Sets a compatible version constraint (~=).
func (v *VersionEditor) SetCompatibleVersion(req *models.Requirement, version string) (*models.Requirement, error)AppendVersionSpecifier()
Adds additional version constraints.
func (v *VersionEditor) AppendVersionSpecifier(req *models.Requirement, specifier string) (*models.Requirement, error)Example
editor := editor.NewVersionEditor()
req := &models.Requirement{
Name: "flask",
Version: ">=1.0.0",
}
// Set exact version
updatedReq, err := editor.SetExactVersion(req, "2.0.1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Updated: %s %s\n", updatedReq.Name, updatedReq.Version)
// Output: Updated: flask ==2.0.1Best Practices
Choosing the Right Editor
Use PositionAwareEditor for:
- Production deployments
- Security updates
- Minimal diff requirements
- CI/CD pipelines
Use VersionEditorV2 for:
- Development tools
- Package management utilities
- Complex editing operations
- Adding/removing packages
Use VersionEditor for:
- Simple scripts
- Basic version updates
- Learning and prototyping
Performance Optimization
// Efficient: Reuse editor instances
editor := editor.NewPositionAwareEditor()
for _, file := range files {
doc, err := editor.ParseRequirementsFile(file.Content)
// Process...
}
// Efficient: Batch updates
updates := map[string]string{
"package1": "==1.0.0",
"package2": "==2.0.0",
"package3": "==3.0.0",
}
err := editor.BatchUpdateVersions(doc, updates)
// Less efficient: Individual updates
for pkg, version := range updates {
err := editor.UpdatePackageVersion(doc, pkg, version) // ❌
}Error Handling
// Handle package not found
err := editor.UpdatePackageVersion(doc, "nonexistent", "==1.0.0")
if err != nil {
if strings.Contains(err.Error(), "not found") {
log.Printf("Package not found, skipping: %v", err)
} else {
log.Fatalf("Update failed: %v", err)
}
}
// Handle batch update failures
err = editor.BatchUpdateVersions(doc, updates)
if err != nil {
log.Printf("Some updates failed: %v", err)
// Continue with successful updates
}Thread Safety
- Editor instances are safe for concurrent use
- Document objects are NOT thread-safe
// Safe: Multiple goroutines using same editor
editor := editor.NewPositionAwareEditor()
go func() { doc, _ := editor.ParseRequirementsFile(content1) }()
go func() { doc, _ := editor.ParseRequirementsFile(content2) }()
// NOT safe: Sharing document between goroutines
doc, _ := editor.ParseRequirementsFile(content)
go func() { editor.UpdatePackageVersion(doc, "pkg1", "==1.0") }() // ❌
go func() { editor.UpdatePackageVersion(doc, "pkg2", "==2.0") }() // ❌Next Steps
- Parser API - Learn about parsing requirements
- Models API - Understand the data structures
- Examples - See practical usage examples