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 a consistent organizational pattern for both module-level functions and class methods. This creates predictable code structure that improves navigation and reduces cognitive overhead when reading code.
Sorting Rules
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}")
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 privacy detection to suggest functions that should be made private.
Detection Algorithm
Skip already private functions (start with
_
)Skip dunder methods (
__method__
)Skip common public API patterns:
Entry points:
main
,run
,execute
Lifecycle:
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
Section Headers (Future Enhancement)
A future enhancement may add support for automatic section header comments:
# Public functions
def calculate_total(items):
pass
def validate_input(data):
pass
# Private functions
def _format_error(error):
pass
def _log_operation(op):
pass
Message Types
The plugin reports three types of 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).
Configuration
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 Command Line Interface 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
Comment Preservation
The auto-fix tool preserves comments associated with functions during reordering:
Before sorting:
After sorting: