Fast linter to detect os.path usage and encourage pathlib adoption
Still using os.path
in 2025+? pathlint
is a fast, comprehensive linter that detects all os.path
usage patterns in your Python codebase and encourages migration to the modern pathlib
module.
- Comprehensive Detection: Catches import statements, aliased imports, function calls, and even type annotations
- Performance Optimized: 3x faster than traditional AST-based linters with early termination
- Smart Exclusions: Automatically skips
venv
,__pycache__
,node_modules
, and other common directories - Professional Output: Clean, informative output with optional statistics
- Auto-fix Support: Experimental auto-fixer to migrate code automatically
pip install pathlint
# Lint files or directories
pathlint myfile.py
pathlint src/
pathlint .
# Multiple paths
pathlint src/ tests/ scripts/
# Show statistics about worst offenders
pathlint . --stats
# Aggressive mode (for fun)
pathlint . --aggressive
# Auto-fix mode (experimental)
pathlint --dry-run src/ # Preview changes
pathlint --fix src/ # Apply fixes
pathlint catches ALL these patterns:
# Import patterns
import os.path
import os.path as ospath
from os import path
from os import path as p
from os.path import join, exists
# Direct usage
os.path.exists('file.txt')
os.path.join('dir', 'file')
path.dirname(__file__) # After 'from os import path'
# Type annotations (missed by most linters!)
def process(f: os.path.PathLike):
pass
def get_path() -> os.path.PathLike:
return 'test'
✓ 42 files checked - no os.path usage found!
/path/to/file.py
L 1: import os.path
L 23: x = os.path.join('a', 'b')
L 45: def process(f: os.path.PathLike):
────────────────────────────────────────
Files checked: 42
Files with issues: 3
Total violations: 7
✗ Found os.path usage. Migrate to pathlib.
Worst offenders:
12 - legacy_utils.py
5 - old_config.py
2 - setup.py
0
- No os.path usage found1
- os.path usage detected2
- Error (no files found, invalid paths, etc.)
Optimized for speed with:
- Early termination for files without 'os' or 'path' strings
- Smart directory traversal with automatic exclusions
- Single-pass AST visitor
- Automatic deduplication of findings
Benchmarks on real codebases:
100 files: 0.31s (vs 0.84s traditional)
500 files: 1.1s (vs 4.2s traditional)
Pathlint can automatically migrate common os.path patterns:
# Preview changes without modifying files
pathlint --dry-run myfile.py
# Apply fixes (modifies files!)
pathlint --fix myfile.py
# Fix entire directory
pathlint --fix src/
Supports migration of:
- Import statements
- Common function calls (
exists
,join
,dirname
, etc.) - Path attributes
- Automatic
pathlib
import addition
# Install with dev dependencies
pip install -e .[dev,test]
# Run tests
pytest
# Format code
ruff format .
# Check linting
ruff check --fix .
pathlib
provides:
- Object-oriented interface
- Operator overloading (
/
for joining paths) - Cross-platform compatibility
- Rich path manipulation methods
- Type safety with
Path
objects
# Old way (os.path)
import os.path
filepath = os.path.join(os.path.dirname(__file__), 'data', 'config.json')
if os.path.exists(filepath):
abs_path = os.path.abspath(filepath)
# Modern way (pathlib)
from pathlib import Path
filepath = Path(__file__).parent / 'data' / 'config.json'
if filepath.exists():
abs_path = filepath.resolve()
Note: While pathlib is recommended for most use cases, there are rare scenarios where os.path
might offer better performance1.
MIT License - see LICENSE.txt
Contributions welcome! Please ensure:
- Tests pass:
pytest
- Code is formatted:
ruff format .
- No linting errors:
ruff check .
Remember: Friends don't let friends use os.path
in 2025+!
Footnotes
-
In extremely performance-critical code paths dealing with millions of file operations,
os.path
string operations can be marginally faster than Path object instantiation. However, these edge cases are rare and should only be considered after profiling confirms a bottleneck. ↩