Skip to content

Examples

Progressive examples and tutorials for Python Requirements Parser.

Overview

This section provides practical examples that demonstrate the capabilities of Python Requirements Parser, from basic usage to advanced scenarios.

Example Categories

🚀 Getting Started

📁 File Operations

🎯 Advanced Parsing

✏️ Editing Requirements

Example Structure

Each example includes:

  • 📝 Complete source code - Ready to run Go programs
  • 📋 Sample input files - Real-world requirements.txt examples
  • 🎯 Expected output - What you should see when running the code
  • 💡 Key concepts - Important patterns and best practices
  • 🔗 Related topics - Links to relevant documentation

Quick Navigation

ExampleDifficultyKey FeaturesUse Case
Basic UsageBeginnerParsing, inspectionLearning the basics
Recursive ResolveBeginnerFile referencesMulti-file projects
Environment VariablesIntermediateVariable substitutionDynamic configurations
Special FormatsIntermediateVCS, URLs, extrasComplex dependencies
Advanced OptionsAdvancedGlobal options, constraintsProduction setups
Version Editor V2IntermediateFull editingDevelopment tools
Position Aware EditorAdvancedMinimal diff editingProduction updates

Running the Examples

Prerequisites

bash
# Install Go (1.19 or later)
go version

# Clone the repository
git clone https://github.com/scagogogo/python-requirements-parser.git
cd python-requirements-parser

Run Individual Examples

bash
# Navigate to an example directory
cd examples/01-basic-usage

# Run the example
go run main.go

# Or build and run
go build -o basic-usage .
./basic-usage

Run All Examples

bash
# From the project root
make examples

# Or manually
for dir in examples/*/; do
    echo "Running $dir..."
    (cd "$dir" && go run main.go)
done

Example Highlights

Basic Parsing

go
// Parse a requirements.txt file
parser := parser.New()
reqs, err := parser.ParseFile("requirements.txt")

// Inspect the results
for _, req := range reqs {
    if !req.IsComment && !req.IsEmpty {
        fmt.Printf("Package: %s, Version: %s\n", req.Name, req.Version)
    }
}

Minimal Diff Editing

go
// Use position-aware editor for minimal changes
editor := editor.NewPositionAwareEditor()
doc, err := editor.ParseRequirementsFile(content)

// Update multiple packages at once
updates := map[string]string{
    "flask":   "==2.0.1",
    "django":  ">=3.2.13",
    "requests": ">=2.28.0",
}

err = editor.BatchUpdateVersions(doc, updates)
result := editor.SerializeToString(doc)

Complex Dependencies

go
// Handle VCS, URLs, and special formats
reqs, err := parser.ParseString(`
git+https://github.com/user/project.git@v1.2.3#egg=project
https://example.com/package.whl
django[rest,auth]>=3.2.0; python_version >= "3.7"
-r requirements-dev.txt
`)

for _, req := range reqs {
    switch {
    case req.IsVCS:
        fmt.Printf("VCS: %s (%s)\n", req.URL, req.VCSType)
    case req.IsURL:
        fmt.Printf("URL: %s\n", req.URL)
    case req.IsFileRef:
        fmt.Printf("File: %s\n", req.FileRef)
    case req.Name != "":
        fmt.Printf("Package: %s %s\n", req.Name, req.Version)
    }
}

Real-World Scenarios

CI/CD Security Updates

go
// Automated security updates in CI/CD
func updateSecurityPackages() error {
    editor := editor.NewPositionAwareEditor()
    
    content, err := os.ReadFile("requirements.txt")
    if err != nil {
        return err
    }
    
    doc, err := editor.ParseRequirementsFile(string(content))
    if err != nil {
        return err
    }
    
    // Security updates from vulnerability scanner
    securityUpdates := map[string]string{
        "django":       ">=3.2.13,<4.0.0",
        "requests":     ">=2.28.0",
        "cryptography": ">=39.0.2",
    }
    
    err = editor.BatchUpdateVersions(doc, securityUpdates)
    if err != nil {
        return err
    }
    
    result := editor.SerializeToString(doc)
    return os.WriteFile("requirements.txt", []byte(result), 0644)
}

Development Workflow

go
// Add development dependencies
func setupDevEnvironment() error {
    editor := editor.NewVersionEditorV2()
    
    doc, err := editor.ParseRequirementsFile(productionRequirements)
    if err != nil {
        return err
    }
    
    // Add development tools
    devPackages := map[string][]string{
        "pytest":     {">=7.0.0", nil, `python_version >= "3.7"`},
        "black":      {">=22.0.0", nil, `python_version >= "3.7"`},
        "mypy":       {">=0.950", nil, `python_version >= "3.7"`},
        "pre-commit": {">=2.20.0", nil, `python_version >= "3.7"`},
    }
    
    for name, spec := range devPackages {
        err := editor.AddPackage(doc, name, spec[0], nil, spec[2])
        if err != nil {
            return err
        }
    }
    
    result := editor.SerializeToString(doc)
    return os.WriteFile("requirements-dev.txt", []byte(result), 0644)
}

Package Analysis

go
// Analyze package dependencies
func analyzeRequirements(filename string) error {
    parser := parser.New()
    reqs, err := parser.ParseFile(filename)
    if err != nil {
        return err
    }
    
    stats := struct {
        Total      int
        Packages   int
        VCS        int
        URLs       int
        FileRefs   int
        Comments   int
        WithExtras int
        WithMarkers int
    }{}
    
    for _, req := range reqs {
        stats.Total++
        
        switch {
        case req.IsComment:
            stats.Comments++
        case req.IsVCS:
            stats.VCS++
        case req.IsURL:
            stats.URLs++
        case req.IsFileRef:
            stats.FileRefs++
        case req.Name != "":
            stats.Packages++
            if len(req.Extras) > 0 {
                stats.WithExtras++
            }
            if req.Markers != "" {
                stats.WithMarkers++
            }
        }
    }
    
    fmt.Printf("Requirements Analysis for %s:\n", filename)
    fmt.Printf("  Total lines: %d\n", stats.Total)
    fmt.Printf("  Packages: %d\n", stats.Packages)
    fmt.Printf("  VCS dependencies: %d\n", stats.VCS)
    fmt.Printf("  URL dependencies: %d\n", stats.URLs)
    fmt.Printf("  File references: %d\n", stats.FileRefs)
    fmt.Printf("  Comments: %d\n", stats.Comments)
    fmt.Printf("  With extras: %d\n", stats.WithExtras)
    fmt.Printf("  With markers: %d\n", stats.WithMarkers)
    
    return nil
}

Performance Examples

Batch Processing

go
// Process multiple requirements files efficiently
func processMultipleFiles(files []string) error {
    // Reuse instances for better performance
    parser := parser.New()
    editor := editor.NewPositionAwareEditor()
    
    for _, file := range files {
        start := time.Now()
        
        content, err := os.ReadFile(file)
        if err != nil {
            log.Printf("Failed to read %s: %v", file, err)
            continue
        }
        
        doc, err := editor.ParseRequirementsFile(string(content))
        if err != nil {
            log.Printf("Failed to parse %s: %v", file, err)
            continue
        }
        
        // Process document...
        
        duration := time.Since(start)
        log.Printf("Processed %s in %v", file, duration)
    }
    
    return nil
}

Concurrent Processing

go
// Process files concurrently
func processFilesConcurrently(files []string) error {
    const maxWorkers = 10
    
    semaphore := make(chan struct{}, maxWorkers)
    var wg sync.WaitGroup
    
    for _, file := range files {
        wg.Add(1)
        go func(filename string) {
            defer wg.Done()
            
            semaphore <- struct{}{}
            defer func() { <-semaphore }()
            
            // Each goroutine gets its own instances (thread-safe)
            parser := parser.New()
            editor := editor.NewPositionAwareEditor()
            
            err := processFile(parser, editor, filename)
            if err != nil {
                log.Printf("Failed to process %s: %v", filename, err)
            }
        }(file)
    }
    
    wg.Wait()
    return nil
}

Testing Examples

Unit Testing

go
func TestRequirementsParser(t *testing.T) {
    parser := parser.New()
    
    content := `flask==2.0.1
django>=3.2.0
# This is a comment
requests>=2.25.0  # HTTP library`
    
    reqs, err := parser.ParseString(content)
    if err != nil {
        t.Fatalf("Parse failed: %v", err)
    }
    
    // Verify results
    packages := 0
    comments := 0
    
    for _, req := range reqs {
        if req.IsComment {
            comments++
        } else if req.Name != "" {
            packages++
        }
    }
    
    if packages != 3 {
        t.Errorf("Expected 3 packages, got %d", packages)
    }
    
    if comments != 1 {
        t.Errorf("Expected 1 comment, got %d", comments)
    }
}

Integration Testing

go
func TestEndToEndWorkflow(t *testing.T) {
    // Create temporary requirements file
    content := `flask==1.0.0
django>=3.2.0`
    
    tmpfile, err := os.CreateTemp("", "requirements*.txt")
    if err != nil {
        t.Fatal(err)
    }
    defer os.Remove(tmpfile.Name())
    
    _, err = tmpfile.WriteString(content)
    if err != nil {
        t.Fatal(err)
    }
    tmpfile.Close()
    
    // Test complete workflow
    editor := editor.NewPositionAwareEditor()
    
    fileContent, err := os.ReadFile(tmpfile.Name())
    if err != nil {
        t.Fatal(err)
    }
    
    doc, err := editor.ParseRequirementsFile(string(fileContent))
    if err != nil {
        t.Fatal(err)
    }
    
    err = editor.UpdatePackageVersion(doc, "flask", "==2.0.1")
    if err != nil {
        t.Fatal(err)
    }
    
    result := editor.SerializeToString(doc)
    
    if !strings.Contains(result, "flask==2.0.1") {
        t.Error("Flask version not updated correctly")
    }
    
    if !strings.Contains(result, "django>=3.2.0") {
        t.Error("Django version should remain unchanged")
    }
}

Next Steps

Choose an example that matches your use case:

Additional Resources

Released under the MIT License