0
# Profiles
1
2
Configuration profile system for managing tool settings, rules, and project-specific configurations. Profiles allow customization of Prospector's behavior for different projects and coding styles.
3
4
## Capabilities
5
6
### ProspectorProfile Class
7
8
Main configuration profile class that manages tool settings and rules.
9
10
```python { .api }
11
class ProspectorProfile:
12
pass
13
```
14
15
Represents a configuration profile with tool settings, enabled/disabled rules, and other preferences.
16
17
```python { .api }
18
@staticmethod
19
def load(name: Union[str, Path], profile_path: list[Path],
20
forced_inherits: Optional[list[str]] = None) -> ProspectorProfile
21
```
22
23
Loads a profile from the filesystem or built-in profiles.
24
25
**Parameters:**
26
- `name`: Union[str, Path] - Profile name or path to profile file
27
- `profile_path`: list[Path] - List of directories to search for profiles
28
- `forced_inherits`: Optional[list[str]] - Additional profiles to inherit from
29
30
**Returns:**
31
- `ProspectorProfile` - Loaded and configured profile
32
33
**Raises:**
34
- `ProfileNotFound` - If the specified profile cannot be found
35
- `CannotParseProfile` - If the profile file contains invalid YAML
36
37
**Profile Search Order:**
38
1. Explicit path if `name` is a Path
39
2. Directories in `profile_path` list
40
3. Built-in profiles directory
41
4. Python packages named `prospector_profile_{module_name}`
42
43
```python { .api }
44
def is_tool_enabled(self, tool_name: str) -> Optional[bool]
45
```
46
47
Checks if a specific tool is enabled in this profile.
48
49
**Parameters:**
50
- `tool_name`: str - Name of the tool to check
51
52
**Returns:**
53
- `Optional[bool]` - True if enabled, False if disabled, None if not specified
54
55
```python { .api }
56
def get_disabled_messages(self, tool_name: str) -> list[str]
57
```
58
59
Gets list of message codes disabled for a specific tool.
60
61
**Parameters:**
62
- `tool_name`: str - Name of the tool
63
64
**Returns:**
65
- `list[str]` - List of disabled message codes
66
67
```python { .api }
68
def get_enabled_messages(self, tool_name: str) -> list[str]
69
```
70
71
Gets list of message codes enabled for a specific tool.
72
73
**Parameters:**
74
- `tool_name`: str - Name of the tool
75
76
**Returns:**
77
- `list[str]` - List of enabled message codes
78
79
```python { .api }
80
def list_profiles(self) -> list[str]
81
```
82
83
Lists all profiles that were loaded (including inherited profiles).
84
85
**Returns:**
86
- `list[str]` - List of profile names in inheritance order
87
88
### Profile Properties
89
90
Profile objects have various properties for accessing configuration:
91
92
- `autodetect`: bool - Whether to auto-detect libraries and frameworks
93
- `uses`: list[str] - Explicitly specified libraries/frameworks to use
94
- `ignore_patterns`: list[str] - Regex patterns for files to ignore
95
- `ignore_paths`: list[str] - Specific paths to ignore
96
- `output_format`: str - Default output format
97
- `output_target`: list[str] - Default output target files
98
99
### Auto-Loaded Profiles
100
101
```python { .api }
102
AUTO_LOADED_PROFILES: list[Path]
103
```
104
105
List of profile filenames that Prospector automatically searches for in the working directory.
106
107
**Auto-loaded Profile Files:**
108
- `.landscape.yml` / `.landscape.yaml`
109
- `landscape.yml` / `landscape.yaml`
110
- `.prospector.yml` / `.prospector.yaml`
111
- `prospector.yml` / `prospector.yaml`
112
- `prospector/.prospector.yml` / `prospector/.prospector.yaml`
113
- `prospector/prospector.yml` / `prospector/prospector.yaml`
114
- `.prospector/.prospector.yml` / `.prospector/.prospector.yaml`
115
- `.prospector/prospector.yml` / `.prospector/prospector.yaml`
116
117
### Profile Exceptions
118
119
```python { .api }
120
class ProfileNotFound(Exception):
121
def __init__(self, name: str, profile_path: list[Path]) -> None
122
```
123
124
Raised when a requested profile cannot be found.
125
126
**Properties:**
127
- `name`: str - Name of the profile that wasn't found
128
- `profile_path`: list[Path] - Search paths that were checked
129
130
```python { .api }
131
class CannotParseProfile(Exception):
132
def __init__(self, filepath: Path, parse_error: Any) -> None
133
```
134
135
Raised when a profile file contains invalid YAML or configuration.
136
137
**Properties:**
138
- `filepath`: Path - Path to the problematic profile file
139
- `parse_error`: Any - The underlying parsing error
140
141
```python { .api }
142
def get_parse_message(self) -> str
143
```
144
145
Gets a human-readable error message describing the parse failure.
146
147
## Usage Examples
148
149
### Loading Profiles
150
151
```python
152
from prospector.profiles.profile import ProspectorProfile
153
from pathlib import Path
154
155
# Load default profile
156
try:
157
profile = ProspectorProfile.load("default", [Path("/usr/share/prospector/profiles")])
158
print(f"Loaded profile with {len(profile.list_profiles())} inherited profiles")
159
except Exception as e:
160
print(f"Failed to load profile: {e}")
161
162
# Load custom profile from specific location
163
profile_paths = [
164
Path(".prospector"),
165
Path("/etc/prospector/profiles"),
166
Path.home() / ".config/prospector/profiles"
167
]
168
169
try:
170
profile = ProspectorProfile.load("myproject", profile_paths)
171
print("Loaded custom profile")
172
except Exception as e:
173
print(f"Custom profile not found: {e}")
174
```
175
176
### Checking Tool Configuration
177
178
```python
179
from prospector.profiles.profile import ProspectorProfile
180
from pathlib import Path
181
182
profile = ProspectorProfile.load("default", [Path("/usr/share/prospector/profiles")])
183
184
# Check which tools are enabled
185
tools_to_check = ["pylint", "pyflakes", "mypy", "bandit"]
186
187
for tool in tools_to_check:
188
enabled = profile.is_tool_enabled(tool)
189
if enabled is True:
190
print(f"{tool}: enabled")
191
elif enabled is False:
192
print(f"{tool}: disabled")
193
else:
194
print(f"{tool}: not specified (use default)")
195
196
# Check disabled messages for pylint
197
disabled = profile.get_disabled_messages("pylint")
198
if disabled:
199
print(f"Pylint disabled messages: {disabled}")
200
201
# Check enabled messages for pydocstyle
202
enabled = profile.get_enabled_messages("pydocstyle")
203
if enabled:
204
print(f"Pydocstyle enabled messages: {enabled}")
205
```
206
207
### Creating Profile Files
208
209
```yaml
210
# Example .prospector.yaml file
211
strictness: medium
212
test-warnings: false
213
doc-warnings: true
214
autodetect: true
215
216
uses:
217
- django
218
- celery
219
220
ignore-patterns:
221
- '(^|/)migrations(/|$)'
222
- '\.pb2\.py$'
223
224
ignore-paths:
225
- docs
226
- build
227
- venv
228
229
output-format: grouped
230
231
pylint:
232
disable:
233
- missing-docstring
234
- invalid-name
235
options:
236
max-line-length: 120
237
238
pycodestyle:
239
disable:
240
- E501 # line too long
241
options:
242
max-line-length: 120
243
244
mypy:
245
run: true
246
options:
247
ignore-missing-imports: true
248
```
249
250
### Working with Profile Inheritance
251
252
```python
253
from prospector.profiles.profile import ProspectorProfile
254
from pathlib import Path
255
256
# Load profile with forced inheritance
257
profile = ProspectorProfile.load(
258
"myproject",
259
[Path(".prospector")],
260
forced_inherits=["strictness_high", "django"]
261
)
262
263
# See what profiles were loaded
264
profiles = profile.list_profiles()
265
print(f"Profile inheritance chain: {' -> '.join(profiles)}")
266
267
# Check if specific settings come from inheritance
268
if profile.autodetect:
269
print("Auto-detection is enabled")
270
271
libraries = profile.uses
272
if libraries:
273
print(f"Configured for libraries: {libraries}")
274
```
275
276
### Error Handling
277
278
```python
279
from prospector.profiles.profile import ProspectorProfile, ProfileNotFound, CannotParseProfile
280
from pathlib import Path
281
282
def load_profile_safely(name: str, search_paths: list[Path]) -> ProspectorProfile:
283
try:
284
return ProspectorProfile.load(name, search_paths)
285
except ProfileNotFound as e:
286
print(f"Profile '{e.name}' not found in paths: {e.profile_path}")
287
print("Falling back to default profile")
288
return ProspectorProfile.load("default", search_paths)
289
except CannotParseProfile as e:
290
print(f"Failed to parse profile file {e.filepath}:")
291
print(e.get_parse_message())
292
raise
293
294
# Usage
295
search_paths = [Path("."), Path("/etc/prospector")]
296
profile = load_profile_safely("myproject", search_paths)
297
```
298
299
### Auto-Detection of Profiles
300
301
```python
302
from prospector.profiles import AUTO_LOADED_PROFILES
303
from pathlib import Path
304
import os
305
306
def find_auto_profile(working_dir: Path) -> Optional[Path]:
307
"""Find automatically loaded profile in working directory"""
308
for profile_path in AUTO_LOADED_PROFILES:
309
full_path = working_dir / profile_path
310
if full_path.exists() and full_path.is_file():
311
return full_path
312
return None
313
314
# Check current directory
315
current_dir = Path.cwd()
316
auto_profile = find_auto_profile(current_dir)
317
318
if auto_profile:
319
print(f"Found auto-loaded profile: {auto_profile}")
320
# This profile would be automatically loaded by ProspectorConfig
321
else:
322
print("No auto-loaded profile found")
323
print("Checked for these files:")
324
for profile_path in AUTO_LOADED_PROFILES:
325
print(f" {profile_path}")
326
```
327
328
### Dynamic Profile Configuration
329
330
```python
331
from prospector.profiles.profile import ProspectorProfile
332
from prospector.config import ProspectorConfig
333
from pathlib import Path
334
335
def configure_for_project(project_type: str) -> ProspectorConfig:
336
"""Configure Prospector based on project type"""
337
338
# Determine appropriate profiles
339
if project_type == "django":
340
forced_inherits = ["django", "strictness_medium"]
341
elif project_type == "flask":
342
forced_inherits = ["flask", "strictness_medium"]
343
elif project_type == "library":
344
forced_inherits = ["strictness_high", "doc_warnings"]
345
else:
346
forced_inherits = ["strictness_medium"]
347
348
# Create config with custom profile loading
349
config = ProspectorConfig()
350
351
# Override profile with project-specific settings
352
profile_paths = [
353
Path(".prospector"),
354
Path("/etc/prospector/profiles")
355
]
356
357
try:
358
profile = ProspectorProfile.load(
359
"default",
360
profile_paths,
361
forced_inherits=forced_inherits
362
)
363
config.profile = profile
364
print(f"Configured for {project_type} project")
365
except Exception as e:
366
print(f"Failed to configure for {project_type}: {e}")
367
368
return config
369
370
# Usage
371
django_config = configure_for_project("django")
372
library_config = configure_for_project("library")
373
```
374
375
### Profile Validation
376
377
```python
378
from prospector.profiles.profile import ProspectorProfile
379
from pathlib import Path
380
import yaml
381
382
def validate_profile_file(profile_path: Path) -> bool:
383
"""Validate a profile file before loading"""
384
try:
385
# Check if file exists and is readable
386
if not profile_path.exists():
387
print(f"Profile file does not exist: {profile_path}")
388
return False
389
390
# Try to parse as YAML
391
with open(profile_path, 'r') as f:
392
yaml.safe_load(f)
393
394
# Try to load as Prospector profile
395
ProspectorProfile.load(str(profile_path), [profile_path.parent])
396
397
print(f"Profile file is valid: {profile_path}")
398
return True
399
400
except yaml.YAMLError as e:
401
print(f"YAML syntax error in {profile_path}: {e}")
402
return False
403
except Exception as e:
404
print(f"Profile validation failed for {profile_path}: {e}")
405
return False
406
407
# Validate profile files
408
profile_files = [
409
Path(".prospector.yaml"),
410
Path("profiles/myproject.yaml"),
411
Path("profiles/strict.yaml")
412
]
413
414
for profile_file in profile_files:
415
validate_profile_file(profile_file)
416
```