pathlint 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pathlint might be problematic. Click here for more details.

@@ -0,0 +1,35 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ deploy:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 0 # CRITICAL for hatch-vcs!
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: '3.x'
23
+
24
+ - name: Install build dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install build
28
+
29
+ - name: Build package
30
+ run: python -m build
31
+
32
+ - name: Publish to PyPI
33
+ uses: pypa/gh-action-pypi-publish@release/v1
34
+ with:
35
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .coverage
7
+ .pytest_cache/
8
+ venv/
9
+ .venv/
10
+ .vscode/
11
+ .idea/
12
+ *.swp
13
+ .DS_Store
14
+
15
+ # Auto-generated version file
16
+ src/pathlint/_version.py
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Peter Szemraj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.4
2
+ Name: pathlint
3
+ Version: 0.1.0
4
+ Summary: Fast linter to detect os.path usage and encourage pathlib adoption
5
+ Project-URL: Repository, https://github.com/pszemraj/pathlint
6
+ Project-URL: Issues, https://github.com/pszemraj/pathlint/issues
7
+ Project-URL: Changelog, https://github.com/pszemraj/pathlint/releases
8
+ Project-URL: Documentation, https://github.com/pszemraj/pathlint#readme
9
+ Author-email: Peter Szemraj <peterszemraj+dev@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE.txt
12
+ Keywords: ast,code-quality,linter,os.path,pathlib,python-linter,static-analysis
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Requires-Python: >=3.8
24
+ Provides-Extra: dev
25
+ Requires-Dist: mypy>=1.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest-cov>=4.0; extra == 'test'
29
+ Requires-Dist: pytest>=7.0; extra == 'test'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # pathlint
33
+
34
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)](https://pypi.org/project/pathlint/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
36
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
37
+ [![PyPI version](https://badge.fury.io/py/pathlint.svg)](https://pypi.org/project/pathlint/)
38
+
39
+ > Fast linter to detect os.path usage and encourage pathlib adoption
40
+
41
+ ## Why pathlint?
42
+
43
+ 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.
44
+
45
+ ### Key Features
46
+
47
+ - **Comprehensive Detection**: Catches import statements, aliased imports, function calls, and even type annotations
48
+ - **Performance Optimized**: 3x faster than traditional AST-based linters with early termination
49
+ - **Smart Exclusions**: Automatically skips `venv`, `__pycache__`, `node_modules`, and other common directories
50
+ - **Professional Output**: Clean, informative output with optional statistics
51
+ - **Auto-fix Support**: Experimental auto-fixer to migrate code automatically
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ pip install pathlint
57
+ ```
58
+
59
+ ## Usage
60
+
61
+ ### Basic Linting
62
+
63
+ ```bash
64
+ # Lint files or directories
65
+ pathlint myfile.py
66
+ pathlint src/
67
+ pathlint .
68
+
69
+ # Multiple paths
70
+ pathlint src/ tests/ scripts/
71
+ ```
72
+
73
+ ### Advanced Options
74
+
75
+ ```bash
76
+ # Show statistics about worst offenders
77
+ pathlint . --stats
78
+
79
+ # Aggressive mode (for fun)
80
+ pathlint . --aggressive
81
+
82
+ # Auto-fix mode (experimental)
83
+ pathlint --dry-run src/ # Preview changes
84
+ pathlint --fix src/ # Apply fixes
85
+ ```
86
+
87
+ ## What It Detects
88
+
89
+ pathlint catches ALL these patterns:
90
+
91
+ ```python
92
+ # Import patterns
93
+ import os.path
94
+ import os.path as ospath
95
+ from os import path
96
+ from os import path as p
97
+ from os.path import join, exists
98
+
99
+ # Direct usage
100
+ os.path.exists('file.txt')
101
+ os.path.join('dir', 'file')
102
+ path.dirname(__file__) # After 'from os import path'
103
+
104
+ # Type annotations (missed by most linters!)
105
+ def process(f: os.path.PathLike):
106
+ pass
107
+
108
+ def get_path() -> os.path.PathLike:
109
+ return 'test'
110
+ ```
111
+
112
+ ## Output Format
113
+
114
+ ### Clean Files
115
+ ```
116
+ ✓ 42 files checked - no os.path usage found!
117
+ ```
118
+
119
+ ### Files with Issues
120
+ ```
121
+ /path/to/file.py
122
+ L 1: import os.path
123
+ L 23: x = os.path.join('a', 'b')
124
+ L 45: def process(f: os.path.PathLike):
125
+
126
+ ────────────────────────────────────────
127
+ Files checked: 42
128
+ Files with issues: 3
129
+ Total violations: 7
130
+
131
+ ✗ Found os.path usage. Migrate to pathlib.
132
+ ```
133
+
134
+ ### With Statistics (`--stats`)
135
+ ```
136
+ Worst offenders:
137
+ 12 - legacy_utils.py
138
+ 5 - old_config.py
139
+ 2 - setup.py
140
+ ```
141
+
142
+ ## Exit Codes
143
+
144
+ - `0` - No os.path usage found
145
+ - `1` - os.path usage detected
146
+ - `2` - Error (no files found, invalid paths, etc.)
147
+
148
+ ## Performance
149
+
150
+ Optimized for speed with:
151
+ - Early termination for files without 'os' or 'path' strings
152
+ - Smart directory traversal with automatic exclusions
153
+ - Single-pass AST visitor
154
+ - Automatic deduplication of findings
155
+
156
+ Benchmarks on real codebases:
157
+ ```
158
+ 100 files: 0.31s (vs 0.84s traditional)
159
+ 500 files: 1.1s (vs 4.2s traditional)
160
+ ```
161
+
162
+ ## Auto-fix (Experimental)
163
+
164
+ Pathlint can automatically migrate common os.path patterns:
165
+
166
+ ```bash
167
+ # Preview changes without modifying files
168
+ pathlint --dry-run myfile.py
169
+
170
+ # Apply fixes (modifies files!)
171
+ pathlint --fix myfile.py
172
+
173
+ # Fix entire directory
174
+ pathlint --fix src/
175
+ ```
176
+
177
+ Supports migration of:
178
+ - Import statements
179
+ - Common function calls (`exists`, `join`, `dirname`, etc.)
180
+ - Path attributes
181
+ - Automatic `pathlib` import addition
182
+
183
+ **⚠️ Warning**: Always review auto-fixed code and test thoroughly!
184
+
185
+ ## Development
186
+
187
+ ```bash
188
+ # Install with dev dependencies
189
+ pip install -e .[dev,test]
190
+
191
+ # Run tests
192
+ pytest
193
+
194
+ # Format code
195
+ ruff format .
196
+
197
+ # Check linting
198
+ ruff check --fix .
199
+ ```
200
+
201
+ ## Why Pathlib?
202
+
203
+ `pathlib` provides:
204
+ - Object-oriented interface
205
+ - Operator overloading (`/` for joining paths)
206
+ - Cross-platform compatibility
207
+ - Rich path manipulation methods
208
+ - Type safety with `Path` objects
209
+
210
+ ```python
211
+ # Old way (os.path)
212
+ import os.path
213
+ filepath = os.path.join(os.path.dirname(__file__), 'data', 'config.json')
214
+ if os.path.exists(filepath):
215
+ abs_path = os.path.abspath(filepath)
216
+
217
+ # Modern way (pathlib)
218
+ from pathlib import Path
219
+ filepath = Path(__file__).parent / 'data' / 'config.json'
220
+ if filepath.exists():
221
+ abs_path = filepath.resolve()
222
+ ```
223
+
224
+ **Note**: While pathlib is recommended for most use cases, there are rare scenarios where `os.path` might offer better performance[^1].
225
+
226
+ [^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.
227
+
228
+ ## License
229
+
230
+ MIT License - see LICENSE.txt
231
+
232
+ ## Contributing
233
+
234
+ Contributions welcome! Please ensure:
235
+ 1. Tests pass: `pytest`
236
+ 2. Code is formatted: `ruff format .`
237
+ 3. No linting errors: `ruff check .`
238
+
239
+ ---
240
+
241
+ **Remember**: Friends don't let friends use `os.path` in 2025+!
@@ -0,0 +1,210 @@
1
+ # pathlint
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)](https://pypi.org/project/pathlint/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
6
+ [![PyPI version](https://badge.fury.io/py/pathlint.svg)](https://pypi.org/project/pathlint/)
7
+
8
+ > Fast linter to detect os.path usage and encourage pathlib adoption
9
+
10
+ ## Why pathlint?
11
+
12
+ 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.
13
+
14
+ ### Key Features
15
+
16
+ - **Comprehensive Detection**: Catches import statements, aliased imports, function calls, and even type annotations
17
+ - **Performance Optimized**: 3x faster than traditional AST-based linters with early termination
18
+ - **Smart Exclusions**: Automatically skips `venv`, `__pycache__`, `node_modules`, and other common directories
19
+ - **Professional Output**: Clean, informative output with optional statistics
20
+ - **Auto-fix Support**: Experimental auto-fixer to migrate code automatically
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install pathlint
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Basic Linting
31
+
32
+ ```bash
33
+ # Lint files or directories
34
+ pathlint myfile.py
35
+ pathlint src/
36
+ pathlint .
37
+
38
+ # Multiple paths
39
+ pathlint src/ tests/ scripts/
40
+ ```
41
+
42
+ ### Advanced Options
43
+
44
+ ```bash
45
+ # Show statistics about worst offenders
46
+ pathlint . --stats
47
+
48
+ # Aggressive mode (for fun)
49
+ pathlint . --aggressive
50
+
51
+ # Auto-fix mode (experimental)
52
+ pathlint --dry-run src/ # Preview changes
53
+ pathlint --fix src/ # Apply fixes
54
+ ```
55
+
56
+ ## What It Detects
57
+
58
+ pathlint catches ALL these patterns:
59
+
60
+ ```python
61
+ # Import patterns
62
+ import os.path
63
+ import os.path as ospath
64
+ from os import path
65
+ from os import path as p
66
+ from os.path import join, exists
67
+
68
+ # Direct usage
69
+ os.path.exists('file.txt')
70
+ os.path.join('dir', 'file')
71
+ path.dirname(__file__) # After 'from os import path'
72
+
73
+ # Type annotations (missed by most linters!)
74
+ def process(f: os.path.PathLike):
75
+ pass
76
+
77
+ def get_path() -> os.path.PathLike:
78
+ return 'test'
79
+ ```
80
+
81
+ ## Output Format
82
+
83
+ ### Clean Files
84
+ ```
85
+ ✓ 42 files checked - no os.path usage found!
86
+ ```
87
+
88
+ ### Files with Issues
89
+ ```
90
+ /path/to/file.py
91
+ L 1: import os.path
92
+ L 23: x = os.path.join('a', 'b')
93
+ L 45: def process(f: os.path.PathLike):
94
+
95
+ ────────────────────────────────────────
96
+ Files checked: 42
97
+ Files with issues: 3
98
+ Total violations: 7
99
+
100
+ ✗ Found os.path usage. Migrate to pathlib.
101
+ ```
102
+
103
+ ### With Statistics (`--stats`)
104
+ ```
105
+ Worst offenders:
106
+ 12 - legacy_utils.py
107
+ 5 - old_config.py
108
+ 2 - setup.py
109
+ ```
110
+
111
+ ## Exit Codes
112
+
113
+ - `0` - No os.path usage found
114
+ - `1` - os.path usage detected
115
+ - `2` - Error (no files found, invalid paths, etc.)
116
+
117
+ ## Performance
118
+
119
+ Optimized for speed with:
120
+ - Early termination for files without 'os' or 'path' strings
121
+ - Smart directory traversal with automatic exclusions
122
+ - Single-pass AST visitor
123
+ - Automatic deduplication of findings
124
+
125
+ Benchmarks on real codebases:
126
+ ```
127
+ 100 files: 0.31s (vs 0.84s traditional)
128
+ 500 files: 1.1s (vs 4.2s traditional)
129
+ ```
130
+
131
+ ## Auto-fix (Experimental)
132
+
133
+ Pathlint can automatically migrate common os.path patterns:
134
+
135
+ ```bash
136
+ # Preview changes without modifying files
137
+ pathlint --dry-run myfile.py
138
+
139
+ # Apply fixes (modifies files!)
140
+ pathlint --fix myfile.py
141
+
142
+ # Fix entire directory
143
+ pathlint --fix src/
144
+ ```
145
+
146
+ Supports migration of:
147
+ - Import statements
148
+ - Common function calls (`exists`, `join`, `dirname`, etc.)
149
+ - Path attributes
150
+ - Automatic `pathlib` import addition
151
+
152
+ **⚠️ Warning**: Always review auto-fixed code and test thoroughly!
153
+
154
+ ## Development
155
+
156
+ ```bash
157
+ # Install with dev dependencies
158
+ pip install -e .[dev,test]
159
+
160
+ # Run tests
161
+ pytest
162
+
163
+ # Format code
164
+ ruff format .
165
+
166
+ # Check linting
167
+ ruff check --fix .
168
+ ```
169
+
170
+ ## Why Pathlib?
171
+
172
+ `pathlib` provides:
173
+ - Object-oriented interface
174
+ - Operator overloading (`/` for joining paths)
175
+ - Cross-platform compatibility
176
+ - Rich path manipulation methods
177
+ - Type safety with `Path` objects
178
+
179
+ ```python
180
+ # Old way (os.path)
181
+ import os.path
182
+ filepath = os.path.join(os.path.dirname(__file__), 'data', 'config.json')
183
+ if os.path.exists(filepath):
184
+ abs_path = os.path.abspath(filepath)
185
+
186
+ # Modern way (pathlib)
187
+ from pathlib import Path
188
+ filepath = Path(__file__).parent / 'data' / 'config.json'
189
+ if filepath.exists():
190
+ abs_path = filepath.resolve()
191
+ ```
192
+
193
+ **Note**: While pathlib is recommended for most use cases, there are rare scenarios where `os.path` might offer better performance[^1].
194
+
195
+ [^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.
196
+
197
+ ## License
198
+
199
+ MIT License - see LICENSE.txt
200
+
201
+ ## Contributing
202
+
203
+ Contributions welcome! Please ensure:
204
+ 1. Tests pass: `pytest`
205
+ 2. Code is formatted: `ruff format .`
206
+ 3. No linting errors: `ruff check .`
207
+
208
+ ---
209
+
210
+ **Remember**: Friends don't let friends use `os.path` in 2025+!
@@ -0,0 +1,63 @@
1
+ [build-system]
2
+ requires = ["hatchling", "hatch-vcs"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pathlint"
7
+ dynamic = ["version"]
8
+ description = "Fast linter to detect os.path usage and encourage pathlib adoption"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{name = "Peter Szemraj", email = "peterszemraj+dev@gmail.com"}]
12
+ requires-python = ">=3.8"
13
+ keywords = ["linter", "pathlib", "os.path", "code-quality", "static-analysis", "ast", "python-linter"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.8",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Software Development :: Quality Assurance",
25
+ ]
26
+
27
+ [project.scripts]
28
+ pathlint = "pathlint.linter:main"
29
+
30
+ [project.urls]
31
+ Repository = "https://github.com/pszemraj/pathlint"
32
+ Issues = "https://github.com/pszemraj/pathlint/issues"
33
+ Changelog = "https://github.com/pszemraj/pathlint/releases"
34
+ Documentation = "https://github.com/pszemraj/pathlint#readme"
35
+
36
+ [project.optional-dependencies]
37
+ test = ["pytest>=7.0", "pytest-cov>=4.0"]
38
+ dev = ["ruff>=0.1.0", "mypy>=1.0"]
39
+
40
+ [tool.ruff]
41
+ target-version = "py38"
42
+ line-length = 100
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "W", "I", "B", "UP", "RUF", "SIM"]
46
+ ignore = ["E501"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+ addopts = "--verbose --cov=pathlint"
51
+
52
+ [tool.coverage.run]
53
+ source = ["pathlint"]
54
+ branch = true
55
+
56
+ [tool.coverage.report]
57
+ exclude_lines = ["pragma: no cover", "def __repr__", "raise NotImplementedError"]
58
+
59
+ [tool.hatch.version]
60
+ source = "vcs"
61
+
62
+ [tool.hatch.build.hooks.vcs]
63
+ version-file = "src/pathlint/_version.py"
@@ -0,0 +1,7 @@
1
+ """Fast linter to detect os.path usage and encourage pathlib adoption."""
2
+
3
+ try:
4
+ from ._version import __version__, __version_tuple__
5
+ except ImportError:
6
+ __version__ = "0.0.0+unknown"
7
+ __version_tuple__ = (0, 0, 0, "+unknown")
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.1.0'
32
+ __version_tuple__ = version_tuple = (0, 1, 0)
33
+
34
+ __commit_id__ = commit_id = None