0
# File Finding
1
2
File and module discovery system that identifies Python code to be analyzed. The FileFinder class handles discovery of Python files, modules, and packages while respecting exclusion filters.
3
4
## Capabilities
5
6
### FileFinder Class
7
8
Discovers files and modules to be analyzed by static analysis tools.
9
10
```python { .api }
11
class FileFinder:
12
def __init__(self, *provided_paths: Path,
13
exclusion_filters: Optional[Iterable[Callable[[Path], bool]]] = None) -> None
14
```
15
16
Creates a new FileFinder for discovering Python code.
17
18
**Parameters:**
19
- `*provided_paths`: Path - One or more paths to search (files or directories)
20
- `exclusion_filters`: Optional[Iterable[Callable[[Path], bool]]] - List of filter functions that return True for paths to exclude
21
22
**Built-in Exclusions:**
23
The FileFinder automatically excludes:
24
- Directories: `.git`, `.tox`, `.mypy_cache`, `.pytest_cache`, `.venv`, `__pycache__`, `node_modules`
25
- Virtual environments (detected using heuristics)
26
27
### File Discovery Properties and Methods
28
29
```python { .api }
30
@property
31
def files(self) -> set[Path]
32
```
33
34
Lists every individual file found from the provided paths.
35
36
**Returns:**
37
- `set[Path]` - Set of Path objects for all discovered files
38
39
This property recursively discovers all files in the provided directories, applying exclusion filters.
40
41
```python { .api }
42
@property
43
def python_modules(self) -> list[Path]
44
```
45
46
Lists every Python module file found in the provided paths.
47
48
**Returns:**
49
- `list[Path]` - List of Path objects for Python module files (`.py` files)
50
51
Returns individual Python files that pass the `is_python_module` test.
52
53
```python { .api }
54
@property
55
def python_packages(self) -> list[Path]
56
```
57
58
Lists every directory that is a Python package (contains `__init__.py`).
59
60
**Returns:**
61
- `list[Path]` - List of Path objects for package directories
62
63
```python { .api }
64
@property
65
def directories(self) -> set[Path]
66
```
67
68
Lists every directory found from the provided paths.
69
70
**Returns:**
71
- `set[Path]` - Set of Path objects for all discovered directories
72
73
```python { .api }
74
def is_excluded(self, path: Path) -> bool
75
```
76
77
Checks if a path would be excluded by the configured filters.
78
79
**Parameters:**
80
- `path`: Path - The path to check
81
82
**Returns:**
83
- `bool` - True if the path should be excluded
84
85
### Python Path Management
86
87
```python { .api }
88
def make_syspath(self) -> list[Path]
89
```
90
91
Creates a list of paths suitable for adding to Python's sys.path.
92
93
**Returns:**
94
- `list[Path]` - List of directory paths that should be added to sys.path for proper module resolution
95
96
This method analyzes the provided paths and determines the appropriate parent directories that should be in sys.path to ensure proper module imports during analysis.
97
98
## Usage Examples
99
100
### Basic File Discovery
101
102
```python
103
from pathlib import Path
104
from prospector.finder import FileFinder
105
106
# Discover files in current directory
107
finder = FileFinder(Path("."))
108
109
# Get all Python modules
110
modules = finder.python_modules
111
print(f"Found {len(modules)} Python modules:")
112
for module in modules:
113
print(f" {module}")
114
115
# Get all packages
116
packages = finder.python_packages
117
print(f"Found {len(packages)} packages:")
118
for package in packages:
119
print(f" {package}")
120
```
121
122
### Multiple Path Discovery
123
124
```python
125
from pathlib import Path
126
from prospector.finder import FileFinder
127
128
# Analyze multiple paths
129
paths = [Path("src"), Path("tests"), Path("scripts")]
130
finder = FileFinder(*paths)
131
132
# Iterate over all modules
133
for module_path in finder.python_modules:
134
print(f"Module: {module_path}")
135
136
# Get syspath entries
137
syspath_entries = finder.make_syspath()
138
print(f"Sys.path entries needed: {syspath_entries}")
139
```
140
141
### Custom Exclusion Filters
142
143
```python
144
from pathlib import Path
145
from prospector.finder import FileFinder
146
import re
147
148
# Create custom exclusion filters
149
def exclude_test_files(path: Path) -> bool:
150
"""Exclude test files"""
151
return path.name.startswith("test_") or path.name.endswith("_test.py")
152
153
def exclude_migration_files(path: Path) -> bool:
154
"""Exclude Django migration files"""
155
return "migrations" in path.parts and path.suffix == ".py"
156
157
# Create finder with custom filters
158
finder = FileFinder(
159
Path("src"),
160
exclusion_filters=[exclude_test_files, exclude_migration_files]
161
)
162
163
# Only non-test, non-migration files will be found
164
modules = finder.python_modules
165
print(f"Found {len(modules)} modules (excluding tests and migrations)")
166
```
167
168
### Working with Configuration
169
170
```python
171
from pathlib import Path
172
from prospector.finder import FileFinder
173
from prospector.config import ProspectorConfig
174
175
# Use with ProspectorConfig exclusion filter
176
config = ProspectorConfig()
177
paths = [Path(".")]
178
179
# Create finder with config's exclusion filters
180
finder = FileFinder(*paths, exclusion_filters=[config.make_exclusion_filter()])
181
182
# Get modules that pass all filters
183
modules = finder.python_modules
184
185
# Check what would be included vs excluded
186
print("Included modules:")
187
for module in finder.python_modules:
188
print(f" {module}")
189
190
print("\nAll files (use files property for complete file list):")
191
for file_path in finder.files:
192
print(f" {file_path}")
193
```
194
195
### Specific File Analysis
196
197
```python
198
from pathlib import Path
199
from prospector.finder import FileFinder
200
201
# Analyze specific files
202
specific_files = [
203
Path("src/main.py"),
204
Path("src/utils.py"),
205
Path("tests/test_main.py")
206
]
207
208
finder = FileFinder(*specific_files)
209
210
# When providing specific files, they are all considered modules
211
modules = finder.python_modules
212
print(f"Analyzing {len(modules)} specific files:")
213
for module in modules:
214
print(f" {module}")
215
216
# Syspath still works with specific files
217
syspath = finder.make_syspath()
218
print(f"Syspath entries: {syspath}")
219
```
220
221
### Error Handling
222
223
```python
224
from pathlib import Path
225
from prospector.finder import FileFinder
226
227
try:
228
# This will raise FileNotFoundError if path doesn't exist
229
finder = FileFinder(Path("nonexistent/path"))
230
except FileNotFoundError as e:
231
print(f"Path not found: {e}")
232
233
# Safer approach - check existence first
234
paths_to_check = [Path("src"), Path("lib"), Path("scripts")]
235
valid_paths = [p for p in paths_to_check if p.exists()]
236
237
if valid_paths:
238
finder = FileFinder(*valid_paths)
239
modules = finder.python_modules
240
print(f"Found {len(modules)} modules in {len(valid_paths)} valid paths")
241
else:
242
print("No valid paths found")
243
```
244
245
### Integration with Tools
246
247
```python
248
from pathlib import Path
249
from prospector.finder import FileFinder
250
from prospector.config import ProspectorConfig
251
252
# This is how FileFinder is typically used within Prospector
253
config = ProspectorConfig()
254
255
# Create finder with config's paths and exclusion filters
256
paths = [Path(p) for p in config.paths]
257
finder = FileFinder(*paths, exclusion_filters=[config.make_exclusion_filter()])
258
259
# Tools use the finder to discover files to analyze
260
tools = config.get_tools(finder)
261
262
# Each tool gets the same FileFinder instance
263
for tool in tools:
264
print(f"Running {tool.__class__.__name__}")
265
messages = tool.run(finder)
266
print(f" Found {len(messages)} issues")
267
268
# Tools can access different file views:
269
print(f"Python modules: {len(finder.python_modules)}")
270
print(f"Python packages: {len(finder.python_packages)}")
271
print(f"All files: {len(finder.files)}")
272
print(f"Directories: {len(finder.directories)}")
273
```