Function and Method Sorting Algorithm
This document describes the comprehensive sorting algorithm used by pylint-sort-functions to organize Python code for improved readability and maintainability.
Overview
The plugin enforces consistent organizational patterns for both module-level functions and class methods using a flexible multi-category system. Beyond traditional binary public/private sorting, the plugin supports framework-specific presets and custom categorization that creates predictable code structure improving navigation and reducing cognitive overhead when reading code.
Key Features:
Traditional binary sorting: Public functions before private functions (default)
Multi-category system: Framework-aware method organization (Phase 1)
Framework presets: Built-in configurations for pytest, unittest, PyQt
Custom categorization: JSON-configurable method categories with pattern matching
Functional section headers: Validated headers that enforce organizational structure (Phase 2)
Sorting Rules
Sorting System Overview
The plugin supports two sorting modes:
Binary Mode (Default): Traditional public/private categorization
Multi-Category Mode: Framework-aware categorization with custom categories
Binary Mode provides backward compatibility and follows simple rules:
Public functions (no underscore) sorted alphabetically
Private functions (underscore prefix) sorted alphabetically
Public functions always come before private functions
Multi-Category Mode enables sophisticated method organization:
Multiple categories defined by patterns and decorators
Framework-specific presets (pytest, unittest, pyqt)
Custom JSON category configuration
Priority-based conflict resolution
Optional section header validation
Binary Mode Sorting (Default)
Module-Level Functions:
Functions within a module are organized using the following hierarchy:
Public functions (no underscore prefix) - sorted alphabetically
Private functions (single underscore prefix) - sorted alphabetically
Example:
# Public functions (alphabetically sorted)
def calculate_total(items):
return sum(item.price for item in items)
def format_currency(amount):
return f"${amount:.2f}"
def validate_input(data):
return data and isinstance(data, dict)
# Private functions (alphabetically sorted)
def _format_error_message(error):
return f"Error: {error}"
def _log_operation(operation):
logger.debug(f"Performing: {operation}")
Class Methods:
Methods within classes follow the same organizational pattern:
Public methods (including dunder methods) - sorted alphabetically
Private methods (single underscore prefix) - sorted alphabetically
Example:
class ShoppingCart:
def __init__(self, customer_id):
self.customer_id = customer_id
self.items = []
def __str__(self):
return f"Cart for {self.customer_id} with {len(self.items)} items"
def add_item(self, item):
self.items.append(item)
def calculate_total(self):
return sum(item.price for item in self.items)
def remove_item(self, item_id):
self.items = [item for item in self.items if item.id != item_id]
# Private methods
def _apply_discount(self, amount):
return amount * 0.9
def _log_transaction(self, transaction):
logger.info(f"Transaction: {transaction}")
Multi-Category Mode Sorting (Phase 1)
When enable-method-categories = true is configured, the plugin uses a sophisticated
multi-category system that goes beyond binary public/private classification.
Framework Presets:
Built-in configurations for common Python frameworks:
pytest preset (framework-preset = "pytest"):
class TestUserService:
# Test fixtures (priority: highest)
def setup_method(self):
pass
# Test methods (priority: high)
def test_create_user(self):
pass
def test_delete_user(self):
pass
# Public methods (priority: medium)
def verify_result(self):
pass
# Private methods (priority: lowest)
def _create_test_data(self):
pass
unittest preset (framework-preset = "unittest"):
class TestUserService(unittest.TestCase):
# Lifecycle methods (priority: highest)
def setUp(self):
pass
def tearDown(self):
pass
# Test methods (priority: high)
def test_create_user(self):
pass
# Public methods (priority: medium)
def assert_user_valid(self, user):
pass
# Private methods (priority: lowest)
def _create_test_user(self):
pass
pyqt preset (framework-preset = "pyqt"):
class UserDialog(QDialog):
# Initialization methods (priority: highest)
def __init__(self, parent=None):
super().__init__(parent)
def setup_ui(self):
pass
# Properties (priority: high)
@property
def user_data(self):
return self._user_data
# Event handlers (priority: medium-high)
def closeEvent(self, event):
pass
# Public methods (priority: medium)
def load_data(self):
pass
# Private methods (priority: lowest)
def _validate_input(self):
pass
Custom Category Configuration:
Define your own categories using JSON configuration:
[tool.pylint.function-sort]
enable-method-categories = true
method-categories = '''[
{"name": "initialization", "patterns": ["__init__", "setup*"], "priority": 20},
{"name": "properties", "decorators": ["@property", "@*.setter"], "priority": 15},
{"name": "api_endpoints", "decorators": ["@app.route"], "priority": 10},
{"name": "public_methods", "patterns": ["*"], "priority": 5},
{"name": "private_methods", "patterns": ["_*"], "priority": 1}
]'''
Pattern Matching System:
Categories use flexible pattern matching:
Name patterns: Glob patterns like
test_*,setup*,*EventDecorator patterns: Match decorators like
@property,@app.route,@*.fixturePriority resolution: Higher priority numbers win when patterns conflict
Alphabetical sorting: Methods sorted alphabetically within each category
Category Ordering:
Categories are ordered by priority (highest to lowest), then methods within each
category are sorted alphabetically (or by declaration order if category-sorting = "declaration").
Special Method Handling
Dunder Methods
Dunder methods (__init__, __str__, __call__, etc.) are treated as public methods
and are sorted alphabetically with other public methods. Due to their __ prefix, they
naturally appear at the top of the public methods section.
Rationale: Dunder methods are part of Python’s special method protocol and are considered public API. Their alphabetical ordering ensures consistency while their double-underscore prefix provides natural grouping at the top of classes.
Private vs Public Classification
Public: No underscore prefix (
method_name)Public: Dunder methods (
__method_name__)Private: Single underscore prefix (
_method_name)
Framework Integration
Decorator Exclusions
The plugin supports excluding functions/methods with specific decorators from sorting requirements. This is essential for framework compatibility where decorator order matters.
Common exclusion patterns:
# Click commands - order may matter for help display
@click.command()
def init():
pass
@click.command()
def deploy():
pass
# Flask routes - order may affect route matching
@app.route('/api/users')
def list_users():
pass
@app.route('/api/users/<int:id>')
def get_user(id):
pass
Configuration example:
[tool.pylint.plugins]
load-plugins = ["pylint_sort_functions"]
[tool.pylint."messages control"]
# Enable all sorting checks
enable = ["unsorted-functions", "unsorted-methods", "mixed-function-visibility"]
# Configure decorator exclusions
ignore-decorators = ["@app.route", "@*.command", "@pytest.fixture"]
Privacy Detection
The plugin includes intelligent bidirectional privacy detection to suggest functions that have incorrect privacy levels.
Bidirectional Analysis:
W9004 Detection: Identifies public functions that should be private
W9005 Detection: Identifies private functions that should be public
Detection Algorithm
Skip already private functions (start with
_)Skip dunder methods (
__method__)Skip common public API patterns:
Entry points:
main,run,executeLifecycle:
start,stop,setup,teardown
Analyze cross-module usage via import analysis
Flag functions only used internally as privacy candidates
Example:
# This function would be flagged for privacy
def calculate_tax_rate(income): # Not imported by other modules
return income * 0.15
# This function would NOT be flagged
def main(): # Entry point pattern
pass
# This function would NOT be flagged
def get_user_data(): # Imported by user_service.py
pass
Advanced AST-Based Boundary Detection
The auto-fix tool uses sophisticated AST (Abstract Syntax Tree) analysis to accurately detect boundaries between functions and other module constructs. This ensures proper handling of complex Python patterns.
Accurate Boundary Detection:
The system correctly handles various Python constructs:
# Module-level constructs are properly preserved
import os
CONSTANT = "value"
def function_a():
pass
# Comments and docstrings preserved
"""Module docstring after functions."""
def function_b():
pass
class MyClass:
pass
# Main blocks preserved at end of file
if __name__ == "__main__":
main()
Key Improvements:
AST-Based Analysis: Uses Python’s AST to understand code structure instead of pattern matching
Accurate End Detection: Finds actual function boundaries using AST node information
Main Block Preservation: Correctly handles
if __name__ == "__main__":blocksComplex Constructs: Properly sorts around classes, global variables, and imports
Docstring Handling: Preserves module-level docstrings and comments in correct positions
Automatic Section Headers
The auto-fix tool can automatically insert section header comments to improve code organization and make the visibility separation more explicit.
Configuration
Section headers are configured through the AutoFixConfig class or CLI arguments:
Programmatic Configuration:
from pylint_sort_functions.auto_fix import AutoFixConfig, FunctionSorter
config = AutoFixConfig(
add_section_headers=True, # Enable section headers
public_header="# Public functions", # Header for public functions
private_header="# Private functions", # Header for private functions
public_method_header="# Public methods", # Header for public methods
private_method_header="# Private methods" # Header for private methods
)
sorter = FunctionSorter(config)
sorter.sort_file(Path("myfile.py"))
CLI Configuration:
# Enable section headers with default text
pylint-sort-functions --fix --add-section-headers myfile.py
# Customize header text
pylint-sort-functions --fix --add-section-headers \
--public-header "=== PUBLIC API ===" \
--private-header "=== INTERNAL HELPERS ===" \
myfile.py
# Separate headers for functions vs methods
pylint-sort-functions --fix --add-section-headers \
--public-method-header ">>> Public Methods <<<" \
--private-method-header ">>> Private Methods <<<" \
myfile.py
Custom Section Header Detection
The auto-fix tool can detect and preserve existing section headers using configurable patterns. This prevents duplication when section headers are already present and allows integration with existing code organization styles.
Configuration Options:
config = AutoFixConfig(
add_section_headers=True,
# Detection patterns for custom organizational styles
additional_section_patterns=[
"=== API ===", # Custom delimiter style
"--- Helpers ---", # Different delimiter
"## Core Functions ##", # Markdown-style headers
"*** Private Implementation ***" # Alternative marker
],
# Case sensitivity control (default: case-insensitive)
section_header_case_sensitive=True
)
CLI Usage:
# Add custom detection patterns
pylint-sort-functions --fix --add-section-headers \
--additional-section-patterns "=== API ===" \
--additional-section-patterns "--- Helpers ---" \
myfile.py
# Enable case-sensitive detection
pylint-sort-functions --fix --add-section-headers \
--section-headers-case-sensitive \
myfile.py
Detection Logic:
The tool automatically detects section headers using a comprehensive pattern matching system:
Configured Headers: Patterns from your
public_header,private_header, etc. are automatically includedDefault Patterns: Backward-compatible patterns like “public functions”, “private methods”, etc.
Additional Patterns: Your custom patterns via
additional_section_patternsCase Sensitivity: Configurable case-sensitive or case-insensitive matching
Example - Preserving Existing Headers:
# Before: Existing file with custom headers
"""=== PUBLIC API ==="""
def zebra_function():
return "zebra"
def alpha_function():
return "alpha"
"""=== INTERNAL ==="""
def _private_helper():
return "helper"
# Configuration to detect these headers
config = AutoFixConfig(
add_section_headers=True,
public_header="=== PUBLIC API ===",
private_header="=== INTERNAL ==="
)
# After auto-fix: Headers preserved, functions sorted
"""=== PUBLIC API ==="""
def alpha_function():
return "alpha"
def zebra_function():
return "zebra"
"""=== INTERNAL ==="""
def _private_helper():
return "helper"
When Headers Are Added
Section headers are automatically inserted only when both public and private functions/methods exist in the same scope. This smart behavior ensures headers add value by clearly separating different visibility levels, while avoiding unnecessary headers for single-visibility scopes.
Headers added:
Module with both public and private functions ✓
Class with both public and private methods ✓
Headers NOT added:
Module with only public functions ✗
Module with only private functions ✗
Class with only public methods ✗
Class with only private methods ✗
Examples
Before auto-fix (unsorted mixed functions):
"""User management module."""
def zebra_function():
"""A public function."""
return "zebra"
def alpha_function():
"""Another public function."""
return "alpha"
def _zebra_private():
"""A private helper function."""
return "_zebra"
def _alpha_private():
"""Another private helper."""
return "_alpha"
After auto-fix with section headers enabled:
"""User management module."""
# Public functions
def alpha_function():
"""Another public function."""
return "alpha"
def zebra_function():
"""A public function."""
return "zebra"
# Private functions
def _alpha_private():
"""Another private helper."""
return "_alpha"
def _zebra_private():
"""A private helper function."""
return "_zebra"
Class method example:
class UserService:
"""Service for user management."""
# Public methods
def create_user(self, data):
return self._validate_user_data(data)
def get_user(self, user_id):
return self._fetch_from_db(user_id)
# Private methods
def _fetch_from_db(self, user_id):
# Database access logic
pass
def _validate_user_data(self, data):
# Validation logic
pass
Section Header Validation (Phase 2)
The plugin supports functional section headers that validate method placement within their designated sections. This transforms section headers from decorative comments into enforceable organizational elements that work seamlessly with both binary and multi-category sorting modes.
Overview
Traditional section headers are purely visual:
class MyClass:
# Test methods
def public_method(self): # No validation - header ignored
pass
# Public methods
def test_something(self): # Wrong section - not detected
pass
With section header validation enabled, these headers become functional:
class MyClass:
# Test methods
def test_something(self): # ✅ Correct section
pass
# Public methods
def public_method(self): # ✅ Correct section
pass
Configuration
Section header validation is controlled by three configuration options:
- enforce-section-headers
Enable section header validation. When enabled, methods must appear under the correct section headers according to their categorization.
Default:
false- require-section-headers
Require section headers for all populated categories. When enabled, missing section headers for categories with methods will be flagged as violations.
Default:
false- allow-empty-sections
Allow section headers that have no methods underneath them. When disabled, empty section headers will be flagged as violations.
Default:
true
Usage Examples
Basic section header validation:
[tool.pylint.'pylint-sort-functions']
enforce-section-headers = true
Strict section header requirements:
[tool.pylint.'pylint-sort-functions']
enforce-section-headers = true
require-section-headers = true
allow-empty-sections = false
Framework-specific section validation:
[tool.pylint.'pylint-sort-functions']
enforce-section-headers = true
framework-preset = "pytest" # Enables test method categories
Section Header Detection
The plugin automatically detects section headers based on the configured category definitions. Headers are matched using case-insensitive comparison:
Default Categories (pytest preset):
# Test methods- for methods matchingtest_*patterns# Public methods- for public methods not matching other patterns# Private methods- for methods with underscore prefix
Custom Categories:
You can define custom section headers through the categorization system:
[tool.pylint.'pylint-sort-functions']
enforce-section-headers = true
method-categories = [
{name = "properties", patterns = ["*"], decorators = ["@property"], section_header = "# Properties"},
{name = "lifecycle", patterns = ["__init__", "setup*", "teardown*"], section_header = "# Lifecycle"},
{name = "public_methods", patterns = ["*"], section_header = "# Public API"},
{name = "private_methods", patterns = ["_*"], section_header = "# Private Implementation"}
]
Validation Logic
Method-to-Section Mapping:
The plugin creates a mapping of line numbers to section categories based on detected headers:
class ExampleClass:
# Properties # Section header (line 1)
@property # Lines 2-4 belong to "properties"
def value(self): # section
return self._value #
# Public API # Section header (line 5)
def process_data(self): # Lines 6-7 belong to "public_methods"
pass # section
Violation Detection:
Methods are validated against their expected sections:
Categorize method using configured patterns and decorators
Find section where method is located based on line number
Compare expected vs actual section
Report violation if sections don’t match
Framework Integration
Section header validation works seamlessly with framework presets:
pytest Integration:
class TestUserService:
# Test methods
def test_create_user(self): # ✅ Correct - test method in test section
pass
def test_delete_user(self): # ✅ Correct - test method in test section
pass
# Public methods
def setup_method(self): # ✅ Correct - utility method in public section
pass
PyQt Integration:
class CustomDialog(QDialog):
# Lifecycle methods
def __init__(self, parent=None): # ✅ Correct - initialization
super().__init__(parent)
# Event handlers
def closeEvent(self, event): # ✅ Correct - event handler section
pass
# Public methods
def set_data(self, data): # ✅ Correct - public API
pass
Error Messages
Section header validation adds three new message types to provide precise error reporting:
- method-wrong-section (W9007)
Method appears in incorrect section according to its categorization.
- missing-section-header (W9008)
Required section header is missing for a populated category.
- empty-section-header (W9009)
Section header exists but contains no methods.
Example Error Output:
example.py:15:4: W9007: Method 'test_something' should be in 'test_methods' section but found in 'public_methods' section (method-wrong-section)
example.py:1:0: W9008: Missing required section header for category 'test_methods' (missing-section-header)
example.py:8:0: W9009: Empty section header 'properties' - no methods found in this category (empty-section-header)
Benefits
- Enforced Organization:
Section headers become functional elements that enforce intended code organization.
- Clear Error Messages:
Precise reporting shows expected vs actual section placement with line numbers.
- Framework Compatibility:
Works with existing framework presets and custom categorization schemes.
- Progressive Adoption:
Can be enabled gradually - disabled by default for backward compatibility.
- IDE Integration:
Error messages include line numbers for immediate navigation to violations.
Message Types
The plugin reports eight types of violations:
Sorting Violations:
W9001: unsorted-functions
Functions in a module are not sorted alphabetically within their visibility scope.
W9002: unsorted-methods
Methods in a class are not sorted alphabetically within their visibility scope.
W9003: mixed-function-visibility
Public and private functions are not properly separated (public must come before private).
Privacy Violations:
W9004: function-should-be-private
A public function should be marked as private (add underscore prefix) because it’s only used within its own module.
Example:
# This function is only called within this module
def calculate_internal_hash(data): # W9004: Should be _calculate_internal_hash
return hashlib.md5(data.encode()).hexdigest()
W9005: function-should-be-public
A private function should be made public (remove underscore prefix) because it’s imported and used by other modules.
Example:
# utils.py
def _format_currency(amount): # W9005: Should be format_currency
return f"${amount:.2f}"
# main.py imports it:
from utils import _format_currency # External usage detected
Section Header Violations:
W9006: method-wrong-section
A method appears in the incorrect section according to its categorization. This violation occurs when section header validation is enabled and methods are not positioned under their expected section headers.
Example:
class TestClass:
# Test methods
def public_method(self): # W9006: Should be in 'public_methods' section
pass
# Public methods
def test_something(self): # W9006: Should be in 'test_methods' section
pass
W9007: missing-section-header
A required section header is missing for a populated category. This violation occurs when require-section-headers is enabled and methods exist for a category but no corresponding section header is found.
Example:
class TestClass:
# Missing "# Test methods" header
def test_something(self): # W9007: Missing section header for 'test_methods'
pass
def test_another(self): # Methods exist but no header
pass
W9008: empty-section-header
A section header exists but contains no methods. This violation occurs when allow-empty-sections is disabled and section headers are present without any corresponding methods.
Example:
class TestClass:
# Test methods # W9008: Empty section header
# No test methods defined
# Public methods
def public_method(self):
pass
PyLint Integration
See PyLint Configuration for complete configuration options including:
Enabling/disabling specific message types
Configuring decorator exclusion patterns
Setting up auto-fix integration
CLI Tool
See Auto-Fix Tool Reference for information about the standalone pylint-sort-functions command-line tool
that provides auto-fix functionality independent of PyLint.
Benefits
Consistency
Predictable function/method location
Reduced time searching for specific functions
Easier code reviews and maintenance
Readability
Public API clearly separated from internal implementation
Alphabetical ordering eliminates arbitrary placement decisions
Natural grouping of related functionality
Maintainability
New functions have obvious placement location
Refactoring becomes more systematic
Codebase-wide organizational standards
Algorithm Safety and Robustness
Critical Issue Resolution (v1.3.1+)
GitHub Issue #25 Resolution
Version 1.3.1 includes a comprehensive fix for a critical algorithm safety issue that could cause syntax corruption when auto-sorting files with multiple complex class definitions.
The Problem
The original algorithm processed classes sequentially, which caused line number corruption when multiple classes were present:
# BEFORE: This would cause corruption
class DialogA:
def z_method(self):
super().__init__()
pass
def a_method(self):
pass
class DialogB:
def z_method(self): # Same name as DialogA
pass
def a_method(self): # Same name as DialogA
pass
Result: Class DialogB definition would be lost, methods orphaned, syntax errors created.
The Solution
Multi-Class Safe Processing Algorithm:
Upfront Data Extraction: Extract ALL class information before ANY modifications
Reverse Processing Order: Process classes from end-to-start to preserve line numbers
Mandatory Syntax Validation: Validate output and automatically rollback on errors
Class Boundary Preservation: Ensure all class definitions remain intact
# NEW ALGORITHM (simplified pseudocode):
def _sort_class_methods(self, content, module, lines):
# PHASE 1: Extract all class data upfront (NO modifications yet)
class_info = []
for node in module.body:
if isinstance(node, nodes.ClassDef):
method_spans = self._extract_method_spans(methods, lines, node)
sorted_spans = self._sort_function_spans(method_spans)
class_info.append((node, method_spans, sorted_spans))
# PHASE 2: Process in REVERSE ORDER (preserves line numbers)
for _, original_spans, sorted_spans in reversed(class_info):
content = self._reconstruct_class_with_sorted_methods(
content, original_spans, sorted_spans
)
return content
Syntax Validation with Auto-Rollback:
Every auto-sort operation now includes mandatory validation:
def _validate_syntax_and_rollback(self, file_path, original_content, new_content):
"""Critical safety measure to prevent corruption."""
try:
compile(new_content, str(file_path), 'exec')
return new_content
except SyntaxError as e:
print(f"WARNING: Auto-sort would create syntax error in {file_path}:")
print(f" Error: {e}")
print(" Reverting to original content to prevent file corruption.")
return original_content
Safety Guarantees
The enhanced algorithm provides multiple safety layers:
- 1. Data Integrity
No class definitions are ever lost
Method context is always preserved
super() calls remain properly associated with their classes
- 2. Automatic Error Recovery
Syntax validation after every transformation
Automatic rollback to original content on any error
Detailed error reporting with line numbers and context
- 3. Multi-Class Robustness
Handles complex inheritance hierarchies (PyQt, Django, etc.)
Preserves methods with identical names across different classes
Maintains proper indentation and class boundaries
- 4. Production Safety
Zero data loss risk - files are never left in corrupted state
Backward compatibility - simple cases continue to work as before
Comprehensive test coverage for complex scenarios
Example: Safe Complex Class Processing
Input (Complex Class Hierarchy):
class LicenseSelectionDialog(QDialog):
'''Complex PyQt dialog with inheritance.'''
def setup_ui(self):
'''Setup the user interface.'''
pass
def __init__(self, task, parent=None):
'''Initialize dialog.'''
super().__init__(parent) # CRITICAL: Must preserve class context
self.task = task
def accept(self):
'''Accept dialog.'''
self.result_value = "accepted"
def _validate_input(self):
'''Private validation method.'''
return True
class AnotherDialog(QDialog):
'''Another dialog with same method names.'''
def accept(self): # Same name as above - previously caused corruption
'''Accept this dialog.'''
pass
def __init__(self, parent=None):
'''Initialize this dialog.'''
super().__init__(parent) # This also would break
def _helper_method(self):
'''Private helper.'''
pass
Output (Safely Sorted):
class LicenseSelectionDialog(QDialog):
'''Complex PyQt dialog with inheritance.'''
def __init__(self, task, parent=None):
'''Initialize dialog.'''
super().__init__(parent) # ✓ Preserved in correct class context
self.task = task
def accept(self):
'''Accept dialog.'''
self.result_value = "accepted"
def setup_ui(self):
'''Setup the user interface.'''
pass
def _validate_input(self):
'''Private validation method.'''
return True
class AnotherDialog(QDialog):
'''Another dialog with same method names.'''
def __init__(self, parent=None):
'''Initialize this dialog.'''
super().__init__(parent) # ✓ Preserved in correct class context
def accept(self): # ✓ No longer conflicts with LicenseSelectionDialog.accept
'''Accept this dialog.'''
pass
def _helper_method(self):
'''Private helper.'''
pass
Key Improvements Demonstrated:
✅ Both class definitions preserved intact
✅ Methods sorted within their respective classes
✅ super() calls maintain proper class context
✅ Method name conflicts resolved (accept() in both classes is now safe)
✅ Public/private method separation maintained in each class
User Experience
Before Fix: Silent corruption, manual git restore required After Fix: Safe operation with helpful warnings
$ pylint-sort-functions --fix --auto-sort complex_file.py
If any issues occur (extremely rare), you will see output like:
WARNING: Auto-sort would create syntax error in complex_file.py:
Error: invalid syntax (complex_file.py, line 25)
Line 25: class BrokenClass
Reverting to original content to prevent file corruption.
The file remains unchanged and safe.
Compatibility
Supported Complex Patterns:
✅ PyQt/PySide Applications: Dialog classes, widget hierarchies
✅ Django Projects: Model classes, view classes with complex inheritance
✅ Flask Applications: Multiple route handler classes
✅ FastAPI Projects: Complex dependency injection patterns
✅ Data Science: Classes with complex method interdependencies
✅ Any Framework: Multi-class files with inheritance and super() calls
Testing Coverage:
The fix includes comprehensive test coverage for:
Complex multi-class inheritance scenarios
Methods with identical names across classes
super() call preservation in complex hierarchies
Multi-line method signatures and complex arguments
Mixed public/private method visibility patterns
Error recovery and rollback scenarios
For technical details, see the test suite in tests/test_issue25_syntax_corruption.py.
Technical Implementation Details
Root Cause Analysis:
The original bug occurred because:
Sequential Processing: Classes were processed one-by-one in order
Content Modification: Each class modification changed line numbers for subsequent classes
Stale References: Later classes used outdated line number information
Boundary Loss: Method extraction from wrong positions caused class boundaries to dissolve
Fix Implementation:
Two-Phase Processing: - Phase 1: Extract all class and method information using current line numbers - Phase 2: Apply modifications in reverse order to preserve line number validity
Comprehensive Validation: - Syntax compilation test after every transformation - Automatic rollback mechanism on any detected error - Detailed logging for troubleshooting
Robust Error Handling: - Multiple fallback layers for different error types - Preservation of original file in all error scenarios - Clear user communication about any issues
Performance Impact:
The safety improvements have minimal performance impact:
Small files: No measurable difference
Large files: <5% processing time increase
Complex files: Better reliability far outweighs minimal performance cost
Future Maintenance:
The enhanced algorithm is designed for long-term maintainability:
Comprehensive test coverage prevents regressions
Clear separation of concerns makes modifications safer
Detailed documentation aids future development
Robust error handling provides diagnostic information for edge cases
Comment Preservation
The auto-fix tool preserves comments associated with functions during reordering:
Before sorting:
After sorting: