0
# Path Utilities
1
2
Path matching and validation utilities for file processing in plugins. This API provides convenient functions for filtering and validating file paths based on patterns and file types.
3
4
## Capabilities
5
6
### Python Path Validation
7
8
Check if paths represent Python source files with proper extension matching.
9
10
```python { .api }
11
def is_python_path(path: Optional[Path]) -> bool:
12
"""
13
Function to check if path is a Python file.
14
15
Args:
16
path: A path to a file
17
18
Returns:
19
True if path is a Python file (ends with .py), False otherwise
20
"""
21
```
22
23
**Usage Examples:**
24
25
```python
26
from autohooks.api.path import is_python_path
27
from autohooks.api.git import get_staged_status
28
from pathlib import Path
29
30
def precommit(config, report_progress, **kwargs):
31
# Get staged files
32
staged_files = get_staged_status()
33
34
# Filter for Python files using utility
35
python_files = []
36
for status_entry in staged_files:
37
if is_python_path(status_entry.path):
38
python_files.append(status_entry)
39
40
# Process Python files
41
for status_entry in python_files:
42
process_python_file(status_entry.absolute_path())
43
44
return 0
45
46
# Direct path checking
47
test_paths = [
48
Path("script.py"), # True
49
Path("module.py"), # True
50
Path("README.md"), # False
51
Path("config.toml"), # False
52
None # False
53
]
54
55
for path in test_paths:
56
result = is_python_path(path)
57
print(f"{path}: {result}")
58
```
59
60
### Pattern Matching
61
62
Match file paths against multiple patterns using fnmatch-style glob patterns with support for complex filtering rules.
63
64
```python { .api }
65
def match(path: PathLike, pattern_list: Iterable[str]) -> bool:
66
"""
67
Check if a path like object matches to one of the patterns.
68
69
Internally fnmatch is used.
70
See https://docs.python.org/3/library/fnmatch.html for details.
71
72
Args:
73
path: PathLike to check if it matches to one of the patterns
74
pattern_list: Iterable (e.g tuple or list) of patterns to match against the path
75
76
Returns:
77
True if path matches a pattern of the list, False otherwise
78
"""
79
```
80
81
**Usage Examples:**
82
83
```python
84
from autohooks.api.path import match
85
from autohooks.api.git import get_staged_status
86
from pathlib import Path
87
88
def precommit(config, report_progress, **kwargs):
89
# Get staged files
90
staged_files = get_staged_status()
91
92
# Define patterns for different file types
93
python_patterns = ["*.py", "*.pyi"]
94
config_patterns = ["*.toml", "*.yaml", "*.yml", "*.json"]
95
documentation_patterns = ["*.md", "*.rst", "*.txt"]
96
97
python_files = []
98
config_files = []
99
doc_files = []
100
101
for status_entry in staged_files:
102
path = status_entry.path
103
104
if match(path, python_patterns):
105
python_files.append(status_entry)
106
elif match(path, config_patterns):
107
config_files.append(status_entry)
108
elif match(path, documentation_patterns):
109
doc_files.append(status_entry)
110
111
# Process different file types
112
if python_files:
113
info(f"Processing {len(python_files)} Python files")
114
115
if config_files:
116
info(f"Processing {len(config_files)} config files")
117
118
return 0
119
120
# Pattern matching examples
121
test_files = [
122
"src/main.py",
123
"tests/test_module.py",
124
"docs/README.md",
125
"pyproject.toml",
126
"requirements.txt"
127
]
128
129
# Match Python files
130
python_patterns = ["*.py", "**/*.py"]
131
for filepath in test_files:
132
if match(Path(filepath), python_patterns):
133
print(f"Python file: {filepath}")
134
135
# Match configuration files
136
config_patterns = ["*.toml", "*.yaml", "*.yml", "*.json", "*.ini"]
137
for filepath in test_files:
138
if match(Path(filepath), config_patterns):
139
print(f"Config file: {filepath}")
140
141
# Match by directory patterns
142
test_patterns = ["tests/**", "test_*"]
143
for filepath in test_files:
144
if match(Path(filepath), test_patterns):
145
print(f"Test file: {filepath}")
146
```
147
148
### Advanced Pattern Usage
149
150
Complex pattern matching scenarios for sophisticated file filtering.
151
152
**Usage Examples:**
153
154
```python
155
from autohooks.api.path import match, is_python_path
156
from autohooks.api.git import get_staged_status
157
158
def precommit(config, report_progress, **kwargs):
159
staged_files = get_staged_status()
160
161
# Plugin configuration patterns
162
plugin_config = config.get("tool", "my-plugin")
163
include_patterns = plugin_config.get_value("include", ["*.py"])
164
exclude_patterns = plugin_config.get_value("exclude", [])
165
166
# Filter files based on patterns
167
target_files = []
168
for status_entry in staged_files:
169
path = status_entry.path
170
171
# Check include patterns
172
if match(path, include_patterns):
173
# Check exclude patterns
174
if exclude_patterns and match(path, exclude_patterns):
175
continue # Skip excluded files
176
target_files.append(status_entry)
177
178
# Advanced filtering combining utilities
179
python_files = [
180
entry for entry in target_files
181
if is_python_path(entry.path)
182
]
183
184
# Pattern-based exclusions
185
test_patterns = ["test_*.py", "*_test.py", "tests/**/*.py"]
186
non_test_files = [
187
entry for entry in python_files
188
if not match(entry.path, test_patterns)
189
]
190
191
return 0
192
193
# Complex pattern examples
194
complex_patterns = [
195
"src/**/*.py", # Python files in src directory tree
196
"!src/**/test_*.py", # Exclude test files (Note: fnmatch doesn't support !)
197
"*.{py,pyi}", # Python implementation and interface files
198
"[Mm]akefile*", # Makefiles with different cases
199
"config.{yml,yaml,json}" # Multiple config file extensions
200
]
201
202
# Note: For exclusion patterns, you need to handle them separately
203
include_patterns = ["src/**/*.py", "*.py"]
204
exclude_patterns = ["test_*.py", "*_test.py", "tests/**"]
205
206
def should_process_file(path: Path) -> bool:
207
"""Check if file should be processed based on include/exclude patterns."""
208
# Must match include patterns
209
if not match(path, include_patterns):
210
return False
211
212
# Must not match exclude patterns
213
if match(path, exclude_patterns):
214
return False
215
216
return True
217
```
218
219
### Integration with Git Operations
220
221
Combining path utilities with git operations for comprehensive file filtering.
222
223
**Usage Examples:**
224
225
```python
226
from autohooks.api.path import is_python_path, match
227
from autohooks.api.git import get_staged_status, is_staged_status
228
229
def precommit(config, report_progress, **kwargs):
230
# Get all status entries
231
all_status = get_staged_status()
232
233
# Multi-stage filtering
234
processable_files = []
235
236
for status_entry in all_status:
237
# Only staged files
238
if not is_staged_status(status_entry):
239
continue
240
241
# Only Python files
242
if not is_python_path(status_entry.path):
243
continue
244
245
# Apply custom patterns
246
source_patterns = ["src/**/*.py", "lib/**/*.py"]
247
if not match(status_entry.path, source_patterns):
248
continue
249
250
# Exclude patterns
251
exclude_patterns = ["**/migrations/*.py", "**/__pycache__/**"]
252
if match(status_entry.path, exclude_patterns):
253
continue
254
255
processable_files.append(status_entry)
256
257
if not processable_files:
258
info("No Python source files to process")
259
return 0
260
261
report_progress.init(len(processable_files))
262
263
for status_entry in processable_files:
264
process_python_file(status_entry.absolute_path())
265
report_progress.update()
266
267
return 0
268
```
269
270
## Pattern Syntax
271
272
The `match` function uses Python's `fnmatch` module, which supports Unix shell-style wildcards:
273
274
- `*` - matches everything
275
- `?` - matches any single character
276
- `[seq]` - matches any character in seq
277
- `[!seq]` - matches any character not in seq
278
- `**` - matches directories recursively (when using `pathlib`)
279
280
**Pattern Examples:**
281
282
```python
283
patterns = [
284
"*.py", # All Python files
285
"test_*.py", # Test files starting with test_
286
"*_test.py", # Test files ending with _test
287
"src/**/*.py", # Python files in src directory tree
288
"*.{py,pyi}", # Multiple extensions (not standard fnmatch)
289
"[Tt]est*.py", # Files starting with Test or test
290
"config.?ml", # config.yml, config.xml, etc.
291
]
292
```
293
294
## Types
295
296
```python { .api }
297
from pathlib import Path
298
from typing import Iterable, Optional
299
from os import PathLike
300
```