Skip to main content

Fast linter to detect os.path usage and encourage pathlib adoption

Project description

pathlint

PyPI version pre-commit

Fast linter to detect os.path usage and encourage pathlib adoption

Why pathlint?

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.

Key Features

  • 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

Installation

pip install pathlint

Pre-commit Hook Integration

pathlint integrates with pre-commit to catch os.path usage before commits.

Setup

Add to your .pre-commit-config.yaml:

repos:
  - repo: https://github.com/pszemraj/pathlint
    rev: v0.1.0  # Use latest release
    hooks:
      - id: pathlint

Install the hook:

pre-commit install

Usage

Runs automatically on git commit. Manual check:

pre-commit run pathlint --all-files

Auto-fix Mode (Experimental)

- id: pathlint-fix  # Use auto-fix hook instead

⚠️ Always review auto-fixed changes!

Usage

Basic Linting

# Lint files or directories
pathlint myfile.py
pathlint src/
pathlint .

# Multiple paths
pathlint src/ tests/ scripts/

Advanced Options

# 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

What It Detects

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'

Output Format

Clean Files

✓ 42 files checked - no os.path usage found!

Files with Issues

/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.

With Statistics (--stats)

Worst offenders:
   12 - legacy_utils.py
    5 - old_config.py
    2 - setup.py

Exit Codes

  • 0 - No os.path usage found
  • 1 - os.path usage detected
  • 2 - Error (no files found, invalid paths, etc.)

Performance

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)

Auto-fix (Experimental)

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

⚠️ Warning: Always review auto-fixed code and test thoroughly!

Development

# Install with dev dependencies
pip install -e .[dev]

# Run tests
pytest

# Format code
ruff format .

# Check linting
ruff check --fix .

Why Pathlib?

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 performance[^1].

[^1]: 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.

License

MIT License - see LICENSE.txt

Contributing

Contributions welcome! Please ensure:

  1. Tests pass: pytest
  2. Code is formatted: ruff format .
  3. No linting errors: ruff check .

Remember: Friends don't let friends use os.path in 2025+!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pathlint-0.2.0.tar.gz (16.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pathlint-0.2.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file pathlint-0.2.0.tar.gz.

File metadata

  • Download URL: pathlint-0.2.0.tar.gz
  • Upload date:
  • Size: 16.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pathlint-0.2.0.tar.gz
Algorithm Hash digest
SHA256 00142a1f55e73527f210e6d8a626e69ca5a7588838ee6b46819a8aada99f58a2
MD5 ffe6ef73c22e86f8bf9a3c6ec0f54591
BLAKE2b-256 dd1e8091993a8aa49f3c7087db462302f144255047b4097285513abf7832b8f5

See more details on using hashes here.

File details

Details for the file pathlint-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pathlint-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pathlint-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 367d68770a0d5ed12730ed4823f4b1de7454d0148f9a9e7144637c82be3c5a7c
MD5 a3adf17972b80a7e38e29238a27eee8e
BLAKE2b-256 14d2e9eac509d518344ed838a93867410f6a51dcac7415e5a47fac9fbefef8f6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page