pathlint
Find os.path usage in Python code and migrate supported calls to pathlib.
Example
# Before
import os
filename = os.path.join("src", "file.py")
# With pathlib (keeping a string result)
from pathlib import Path
filename = str(Path("src") / "file.py")
Find places to migrate, then preview the changes pathlint can make:
pathlint check .
pathlint fix . --dry-run
Installation
Requires Python 3.9+. No runtime dependencies.
pip install pathlint
pathlint --version
Quick start
Check a project, a directory, or individual files:
pathlint check .
pathlint check src/ tests/
pathlint check myfile.py
Diagnostics point to the source line so you can review each use:
src/utils.py:12: PL001 os.path usage: os.path.exists(value)
Found 1 violation in 1 of 42 files checked.
Preview experimental fixes without changing files:
pathlint fix . --dry-run
For a file containing import os and the join call, the preview shows:
--- example.py
+++ example.py (fixed)
@@ -1,2 +1,3 @@
import os
-filename = os.path.join("src", "file.py")
+from pathlib import Path as _Path
+filename = str(_Path("src", "file.py"))
Review the diff before applying it, then run your tests:
pathlint fix .
After applying fixes, rerun pathlint check . to see which uses need
manual migration.
List the Python files that any of these commands would select:
pathlint files .
Use this listing to confirm your exclusions before applying fixes; it selects files without reading their Python source.
Paths default to the current directory. Use pathlint --help or
pathlint check --help to explore the CLI; python -m pathlint check . also
works when you prefer module invocation.
What it detects
import os
import os.path as osp
from os.path import exists as present
os.path.exists("file")
osp.join("directory", "file")
present("file")
callback = os.path.join
Pathlint resolves imports, aliases, and local shadowing, including usage in
annotations. Valid os.PathLike annotations are not flagged. Detection is
static: dynamic rebinding and wildcard imports may leave uses undetected.
See detection details for advanced cases.
Auto-fix
Fixing is experimental. Review every diff and run your tests.
Only these five calls support automatic fixes, with string-literal arguments:
| Usage | Pathlib replacement |
|---|---|
join |
str(Path(...)) |
basename |
Path(...).name |
dirname |
str(Path(...).parent) |
isabs |
Path(...).is_absolute() |
expanduser (without ~) |
str(Path(...).expanduser()) |
exists, isfile, isdir |
Manual migration |
abspath, splitext, sep, pathsep |
Manual migration |
Fixes preserve surrounding comments and strings. Annotation usages are detected but never rewritten.
The fixer is conservative: it rewrites calls only when its static checks allow them and leaves uncertain cases unchanged. It does not fix every violation. See fixer limitations for skipped calls and path forms.
Common options
Exclude directories or generated files; repeat --exclude / -e as needed:
pathlint check . --exclude vendor -e "*.generated.py"
Directory scans respect Git ignore rules when Git is available and skip common
generated directories by default. Preview selection with pathlint files .;
see file-selection rules for exact matching behavior.
# Findings only, without a summary
pathlint check . --quiet
# Five files with the most violations
pathlint check . --stats
# Machine-readable report
pathlint check . --output-format json
# Fix preview without a summary or terminal colors
pathlint fix . --dry-run --quiet --no-color
For scripting, check exits 0 when clean and 1 when it finds violations;
fix --dry-run exits 1 when it proposes edits. Errors exit 2.
See the CLI reference for output formats, exit codes, and
migration from older commands.
Pre-commit
Add to .pre-commit-config.yaml, pinning the release you want to use:
repos:
- repo: https://github.com/pszemraj/pathlint
rev: v0.3.0
hooks:
- id: pathlint
args: [--exclude, vendor, --exclude, backups]
Install and run the hook:
pre-commit install
pre-commit run pathlint --all-files
The pathlint hook runs pathlint check. Use pathlint-fix instead to apply
experimental fixes, and review its changes before committing.
Development
Install the development dependencies, run the tests, and check style:
pip install -e '.[dev]'
pytest
ruff check .
ruff format --check .
License
MIT License - see LICENSE.txt.
Metadata
Release files for pathlint 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pathlint-0.3.1.tar.gz | 74.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pathlint-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 107.1 kB
Release files / pathlint-0.3.1.tar.gz
| Download URL | pathlint-0.3.1.tar.gz |
|---|---|
| Size | 74.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7d634932a99c6f710a4e573ff9f64dadaa224178cb2b7cc2d9bbe5736733b0cd
|
|
BLAKE2b-256 checksum How to use checksums |
a7d950c77debc526a25c12882bf22113ee640d90323b023c649cb505038b801c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pathlint-0.3.1-py3-none-any.whl
| Download URL | pathlint-0.3.1-py3-none-any.whl |
|---|---|
| Size | 32.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d641e35e849debef6702512805f6543fb2d5d9ff3635a417d22a87401b46d24c
|
|
BLAKE2b-256 checksum How to use checksums |
91ca185e84cfcfd2419a78d11934cdb2fbbd4839fec781b6cfc613b56a3184fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|