Automatic string literal wrapping for PEP 8 E501 compliance
Project description
e501-fixer
Automatic string literal wrapping for PEP 8 E501 compliance.
Problem
PEP 8 recommends a maximum line length of 88 characters. When strings exceed this limit, Python developers must manually wrap them—a tedious, error-prone process. While tools like Black and ruff exist, none provide comprehensive automatic wrapping of string literals.
e501-fixer fills this gap with a safe, well-tested solution that automatically wraps long strings using Python's implicit string concatenation.
Features
- ✅ Automatic string wrapping using implicit concatenation
- ✅ Preserves quote style (single vs. double quotes)
- ✅ Handles all string types (regular, raw, byte strings)
- ✅ Safe by default (skips f-strings and triple-quoted strings)
- ✅ CLI interface for easy integration into workflows
- ✅ Pre-commit hook support for automated checks
- ✅ 100% test coverage with comprehensive test suite
- ✅ Type-safe with full mypy strict mode compliance
Installation
Via pip (when published to PyPI)
pip install e501-fixer
From source (development)
Clone the standalone e501-fixer repository:
git clone https://github.com/muunkky/e501-fixer.git
cd e501-fixer
pip install -e .
With CLI support
pip install -e ".[cli]"
Quick Start
CLI Usage
# Fix E501 violations in a file
e501-fixer fix myfile.py
# Fix violations in a directory
e501-fixer fix src/
# Check for violations without fixing
e501-fixer check myfile.py
# Check with custom line length
e501-fixer fix --max-line-length 100 myfile.py
Python API
from e501_fixer import FileProcessor
from pathlib import Path
# Create processor with custom line length
processor = FileProcessor(max_line_length=100)
# Process single file
result = processor.process_file(Path("example.py"))
print(f"Fixed {result.fixes_applied} violations")
# Process directory
result = processor.process_directory(Path("src/"))
print(f"Fixed {result.total_fixes} violations in {result.files_processed} files")
How It Works
Before
def greet(name: str) -> None:
message = "Hello, this is a very long string that exceeds the PEP 8 line length limit and needs to be wrapped"
print(message)
After
def greet(name: str) -> None:
message = (
"Hello, this is a very long string that exceeds the "
"PEP 8 line length limit and needs to be wrapped"
)
print(message)
Examples
Preserving Quote Style
# Input: single quotes
description = 'This is a long single-quoted string that needs wrapping and is quite verbose'
# Output: single quotes preserved
description = (
'This is a long single-quoted string that needs wrapping '
'and is quite verbose'
)
Respecting Indentation
# Input: nested string
def setup():
if True:
config = "This is a very long configuration string that exceeds the maximum line length and must be wrapped properly"
# Output: indentation respected
def setup():
if True:
config = (
"This is a very long configuration string that exceeds "
"the maximum line length and must be wrapped properly"
)
Raw Strings
# Input: raw string
pattern = r'This is a very long regex pattern that exceeds the line length and needs to be wrapped for readability'
# Output: raw string type preserved
pattern = (
r'This is a very long regex pattern that exceeds the line '
r'length and needs to be wrapped for readability'
)
Pre-commit Integration
Add to .pre-commit-config.yaml:
- repo: local
hooks:
- id: e501-fixer
name: e501-fixer
entry: e501-fixer fix
language: system
types: [python]
stages: [commit]
Or using the pre-commit hooks defined in this package (when published):
- repo: https://github.com/muunkky/e501-fixer
rev: v0.1.0
hooks:
- id: e501-fix # Auto-fix violations
- id: e501-check # Check without modifying (for CI)
The package provides two hooks:
e501-fix: Automatically fixes E501 violations (use before commit)e501-check: Reports violations without modifying (use in CI/CD)
Safety Guarantees
- No Code Corruption: Conservative approach—skips uncertain cases
- Preserves Semantics: Implicit concatenation produces identical behavior
- Type Safe: Full mypy strict mode compliance
- Well Tested: 90+ tests covering all string types and edge cases
- Documented: Comprehensive architecture documentation (ADR-018)
Architecture
The tool is built on a modular, TDD-driven architecture:
- StringLiteralParser: Identifies and extracts string literals using regex
- BreakPointFinder: Calculates optimal positions to split strings
- StringWrapper: Applies wrapping transformations
- FileProcessor: Orchestrates parsing, wrapping, and file I/O
For detailed architecture, see ARCHITECTURE.md.
Performance
- Per-file: ~1-2ms for average files (100-200 lines)
- Large files: <100ms for 1000+ line files
- Directory processing: Efficient batch handling
Configuration
Line Length
e501-fixer fix --max-line-length 100 myfile.py
Python API:
processor = FileProcessor(max_line_length=100)
Indentation
processor = FileProcessor(default_indentation=" ") # 2 spaces
Limitations
- F-strings: Not wrapped (interpolations make splitting risky)
- Triple-quoted strings: Not wrapped (complex semantics)
- Unbreakable strings: Strings without spaces cannot be wrapped
These are conservative choices that prioritize safety over coverage.
Troubleshooting
No violations found
- Verify the file has strings exceeding the max line length
- Check
--max-line-lengthsetting (default: 88) - Some strings may be intentionally unbreakable (no spaces)
Pre-commit hook not executing
- Ensure
e501-fixeris installed in the pre-commit environment - Check
.pre-commit-config.yamlsyntax - Run
pre-commit installto activate hooks
Import errors
- Install CLI dependencies:
pip install e501-fixer[cli] - Verify Python 3.10+ is installed
Development
Setup
pip install -e ".[dev]"
Testing
pytest tests/
pytest --cov=e501_fixer
Type Checking
mypy e501_fixer/
Linting
ruff check e501_fixer/
License
MIT - see LICENSE file
Contributing
Contributions are welcome! Please ensure:
- All tests pass (
pytest) - Code passes type checking (
mypy) - Code passes linting (
ruff check) - New features include tests and documentation
References
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file e501_fixer-0.1.0.tar.gz.
File metadata
- Download URL: e501_fixer-0.1.0.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ebade9dabd80a638c876cd4eb40220ec63bc44f513453e7bf09b069bb29b1897
|
|
| MD5 |
702ab7deb6bce079b0347595dc9fa307
|
|
| BLAKE2b-256 |
74240660943fcbafe8e43232278bd26412423d71d58f3e1020e61884bb46573a
|
File details
Details for the file e501_fixer-0.1.0-py3-none-any.whl.
File metadata
- Download URL: e501_fixer-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c948c653be312a3e698732768e3c01fbe57219edaa027fdc4647360fcbbb389
|
|
| MD5 |
5e1d007a6533ac82e17b4a6898dc1bac
|
|
| BLAKE2b-256 |
73664bcaabf88a1cbb24984df6957de2fb33fe0a7cbcebab25907ecf3894de9f
|