dissolve-python 0.3.0

A tool to dissolve deprecated calls in Python codebases
Documentation
# Dissolve Design Document

## Overview

Dissolve is a hybrid Python/Rust tool that helps developers migrate from deprecated APIs to their replacements. The core migration and analysis functionality has been rewritten in Rust for improved performance, while the `@replace_me` decorator remains in Python for runtime deprecation warnings.

## Core Purpose

The tool addresses the common problem of API deprecation by providing:
- A Python decorator (`@replace_me`) to mark deprecated functions with runtime warnings
- A Rust-based CLI tool for fast code analysis and transformation
- Automated migration of deprecated function calls to their replacements
- Validation tools to ensure deprecations can be properly migrated
- Version-aware cleanup utilities for library maintainers

## Architecture

### Rust/Python Split

The project maintains a clear separation between Rust and Python components:

**Rust Components** (Performance-critical operations):
- CLI binary and command parsing
- AST parsing using Ruff parser
- Code transformation and migration logic
- Type introspection integration (Pyright/MyPy)
- File scanning and pattern matching
- Deprecated function collection and analysis

**Python Components** (Runtime functionality):
- `@replace_me` decorator for runtime warnings
- Python AST analysis for decorator metadata extraction
- Integration with Python's warning system

### Key Design Decisions

1. **Ruff Parser**: Uses Ruff's Python parser for fast, accurate AST parsing
2. **Type Introspection**: Integrates with Pyright LSP and MyPy daemon for type-aware replacements
3. **Format Preservation**: Maintains original code formatting through careful AST manipulation
4. **No Configuration**: Works out-of-the-box without configuration files
5. **Parallel Processing**: Leverages Rust's concurrency for large codebases

### Core Components (Rust Implementation)

#### 1. CLI Binary (`src/bin/main.rs`)
The main entry point providing four commands:
- `migrate`: Replace deprecated function calls with their replacements
- `cleanup`: Remove deprecated functions based on version constraints
- `check`: Validate that deprecated functions can be migrated
- `info`: List all deprecated functions and their replacements

#### 2. Migration Engine (`src/migrate_ruff.rs`)
Orchestrates the complete migration process:
- Parses Python source using Ruff parser
- Collects deprecated functions from current file and dependencies
- Applies type-aware transformations
- Supports both automatic and interactive modes
- Preserves code formatting through AST manipulation

#### 3. Function Collection (`src/core/ruff_collector.rs`)
Discovers and analyzes `@replace_me` decorated functions:
- Extracts replacement expressions from function bodies
- Handles various Python constructs (functions, methods, properties)
- Collects parameter information and metadata
- Tracks inheritance relationships for method resolution

#### 4. AST Transformation (`src/ruff_parser_improved.rs`)
Performs the actual code transformations:
- Identifies deprecated function calls
- Maps arguments to replacement expressions
- Handles complex cases like method calls, chained calls
- Integrates with type introspection for accurate replacements
- Preserves original code structure and formatting

#### 5. Type Introspection (`src/type_introspection_context.rs`)
Provides type information for accurate replacements:
- **Pyright Integration** (`src/pyright_lsp.rs`): LSP-based type checking
- **MyPy Integration** (`src/mypy_lsp.rs`): Daemon-based type analysis
- Falls back gracefully when type checkers unavailable
- Caches type information for performance

#### 6. Python Decorator (Python Component)
The `@replace_me` decorator remains in Python:
```python
@replace_me(since="1.0.0", remove_in="2.0.0")
def deprecated_function(x, y):
    return new_function(x, y=y)
```
- Provides runtime deprecation warnings
- Uses Python's AST for metadata extraction
- Integrates with Python's warning system

## Key Data Structures

### Core Types (Rust)

```rust
// src/core/types.rs
pub struct ReplaceInfo {
    pub old_name: String,
    pub replacement_expr: String,
    pub replacement_ast: Option<Box<ruff_python_ast::Expr>>,
    pub construct_type: ConstructType,
    pub parameters: Vec<ParameterInfo>,
    pub return_type: Option<String>,
    pub since: Option<String>,
    pub remove_in: Option<String>,
    pub message: Option<String>,
}

pub enum ConstructType {
    Function,
    Property,
    ClassMethod,
    StaticMethod,
    AsyncFunction,
    Class,
    ClassAttribute,
    ModuleAttribute,
}

pub struct ParameterInfo {
    pub name: String,
    pub has_default: bool,
    pub default_value: Option<String>,
    pub is_vararg: bool,   // *args
    pub is_kwarg: bool,    // **kwargs
    pub is_kwonly: bool,   // keyword-only
}
```

### Collection Results

```rust
pub struct CollectionResult {
    pub replacements: HashMap<String, ReplaceInfo>,
    pub unreplaceable: HashMap<String, UnreplaceableConstruct>,
    pub inheritance_map: HashMap<String, Vec<String>>,
}

pub struct UnreplaceableConstruct {
    pub construct_type: ConstructType,
    pub reason: ReplacementFailureReason,
    pub message: String,
}
```

## Workflows

### 1. Migration Workflow (Rust)
```
Python Source File
Ruff Parser → AST Generation → Collect @replace_me functions
Dependency Analysis → Collect functions from imported modules
AST Visitor → Find deprecated calls → Type introspection (if needed)
Argument Mapping → Generate replacement AST → Apply transformations
Code Generation → Format preservation → Output migrated code
```

### 2. Type-Aware Resolution
```
Function Call Found
Check if method call → Query type checker (Pyright/MyPy)
Resolve actual type → Find matching replacement
Apply type-specific transformation
```

### 3. Interactive Mode
```
Find replacement opportunity
Calculate line/column position → Show context to user
Prompt for confirmation → Apply if approved
Continue to next occurrence
```

## Technology Stack

### Rust Dependencies

1. **AST Parsing**
   - `ruff_python_parser`: Fast Python parser from the Ruff project
   - `ruff_python_ast`: AST node definitions
   - `ruff_python_codegen`: Code generation from AST
   - `ruff_text_size`: Text position tracking

2. **CLI and I/O**
   - `clap`: Command-line argument parsing with derive macros
   - `glob`: File pattern matching
   - `anyhow`/`thiserror`: Error handling

3. **Type Checking Integration**
   - `pyo3`: Python interop for MyPy integration
   - Custom LSP clients for Pyright/MyPy
   - `serde`/`serde_json`: LSP message serialization

4. **Utilities**
   - `regex`: Pattern matching for file scanning
   - `once_cell`: Lazy static initialization
   - `tracing`: Structured logging

### Python Components

- Standard library only for the decorator module
- No external dependencies required for runtime functionality

## Advanced Features

### Type-Aware Method Resolution
Uses type checkers to resolve method calls correctly:
```python
# Detects that obj is of type Foo and finds the right replacement
obj = get_foo()
result = obj.deprecated_method()  # Correctly replaced based on type
```

### Inheritance Tracking
Tracks class hierarchies to handle inherited deprecated methods:
```python
class Parent:
    @replace_me(...)
    def old_method(self): ...

class Child(Parent):
    pass

Child().old_method()  # Correctly identifies and replaces
```

### Cross-Module Dependency Analysis
- Recursively analyzes imported modules (configurable depth)
- Builds a complete map of available replacements
- Handles various import styles (from, import as, etc.)

### Parallel Processing
- File discovery and initial scanning done in parallel
- Type checking queries can be batched
- Large codebases processed efficiently

## Error Handling Philosophy

### Graceful Degradation
- Falls back to original code when replacement fails
- Preserves functionality even when migration isn't possible
- Provides detailed error messages for debugging

### Comprehensive Validation
- Checks for complex function bodies that can't be automatically migrated
- Detects recursive calls that would cause infinite loops
- Validates that replacement expressions are syntactically correct

### Preview-First Design
- Default behavior shows changes without applying them
- Requires explicit `--write` flag to modify files
- Provides diff output for review

## CLI Design Philosophy

### Correctness
- Ensures transformations are correct and safe
  (only apply replacements when types match, don't simply )

### Safety First
- Preview mode by default
- Explicit write operations
- Comprehensive validation before changes

### Developer Experience
- Rich help text and examples
- Multiple output formats (diff, summary, detailed)
- Interactive mode for complex migrations
- Batch processing for large codebases

### Integration Friendly
- Exit codes for CI/CD integration
- Machine-readable output options
- Configurable behavior through command-line flags

## Testing Strategy

The Rust implementation includes comprehensive test coverage:
- Unit tests for each component
- Integration tests for complete workflows
- Regression tests for edge cases and bug fixes
- Real-world scenario tests (e.g., Dulwich migration)
- Format preservation validation
- Type checker integration tests

## Python/Rust Boundary

### What Stays in Python

1. **The `@replace_me` decorator** must remain in Python because:
   - It runs at import time in user code
   - It needs to emit Python warnings
   - It must be importable by Python projects
   - It uses Python's AST for runtime analysis

2. **Runtime functionality**:
   - Warning emission
   - Decorator parameter validation
   - Integration with Python's warning filters

### What Moved to Rust

1. **All CLI commands and file processing**
2. **AST parsing and transformation** using Ruff parser
3. **Type checking integration** via LSP/daemon
4. **Performance-critical operations** like file scanning
5. **Cross-module dependency analysis**

### Integration Points

- The Rust tool can read decorator metadata from Python files
- Type checkers are invoked via LSP or daemon protocols
- PyO3 is used for Python interop when needed

## Performance Improvements

The Rust migration provides significant performance benefits:

- **File Scanning**: ~10x faster with parallel processing
- **AST Parsing**: Ruff parser is much faster than Python's ast
- **Memory Usage**: Lower memory footprint
- **Type Checking**: Efficient caching and batching
- **Large Codebases**: Scales better with parallel processing