0
# Pytest Integration
1
2
Seamless integration with pytest for automated code quality checking during testing. Pylama provides a pytest plugin that automatically discovers and checks Python files as part of your test suite.
3
4
## Capabilities
5
6
### Plugin Configuration
7
8
Add pylama checking option to pytest command line interface.
9
10
```python { .api }
11
def pytest_addoption(parser):
12
"""
13
Add --pylama option to pytest command line.
14
15
Args:
16
parser: pytest argument parser
17
18
Adds:
19
--pylama: Enable pylama code quality checks on .py files
20
21
When enabled, pytest will collect all .py files and run pylama
22
checks on them as part of the test suite.
23
"""
24
```
25
26
### File Collection
27
28
Collect Python files for pylama checking during pytest discovery phase.
29
30
```python { .api }
31
def pytest_collect_file(path, parent):
32
"""
33
Collect Python files for pylama checking.
34
35
Args:
36
path: File path being considered for collection
37
parent: Parent pytest collector
38
39
Returns:
40
Optional[PylamaFile]: PylamaFile collector if --pylama enabled and path is .py file
41
42
Only collects files when:
43
- --pylama option is enabled
44
- File has .py extension
45
- File is not excluded by pytest collection rules
46
"""
47
```
48
49
### Session Management
50
51
Manage caching across pytest sessions for performance optimization.
52
53
```python { .api }
54
def pytest_sessionstart(session):
55
"""
56
Initialize pylama session with file modification time caching.
57
58
Args:
59
session: pytest session object
60
61
Sets up caching to skip unchanged files that previously passed
62
pylama checks, improving performance on large codebases.
63
"""
64
65
def pytest_sessionfinish(session):
66
"""
67
Save modification time cache at end of pytest session.
68
69
Args:
70
session: pytest session object
71
72
Persists file modification times to cache for next test run,
73
enabling incremental checking.
74
"""
75
76
def pytest_load_initial_conftests(early_config, *_):
77
"""
78
Register pylama markers before pytest configuration loading.
79
80
Args:
81
early_config: Early pytest configuration
82
83
Registers 'pycodestyle' marker to prevent warnings when using
84
--strict command line argument.
85
"""
86
```
87
88
### Test Implementation
89
90
Core test classes that perform pylama checking within pytest framework.
91
92
```python { .api }
93
class PylamaError(Exception):
94
"""
95
Exception raised when pylama checks fail.
96
97
Contains formatted error messages from pylama checking that
98
will be displayed in pytest output when tests fail.
99
"""
100
101
class PylamaFile(pytest.File):
102
"""
103
Pytest file collector for pylama checks.
104
105
Represents a Python file that will be checked by pylama.
106
Creates a single PylamaItem for the entire file.
107
"""
108
109
def collect(self):
110
"""
111
Create test items for this file.
112
113
Returns:
114
List[PylamaItem]: Single item representing pylama check for this file
115
"""
116
117
class PylamaItem(pytest.Item):
118
"""
119
Pytest test item for individual file pylama checks.
120
121
Represents the actual test that runs pylama on a single file
122
and reports any code quality issues as test failures.
123
"""
124
125
def __init__(self, *args, **kwargs):
126
"""
127
Initialize pylama test item.
128
129
Automatically adds 'pycodestyle' marker and sets up caching
130
infrastructure for performance optimization.
131
"""
132
133
def setup(self):
134
"""
135
Set up test item with caching logic.
136
137
Returns:
138
bool: True if setup successful, False if should skip
139
140
Checks file modification time against cache and skips test
141
if file hasn't changed since last successful check.
142
"""
143
144
def runtest(self):
145
"""
146
Execute pylama check on the file.
147
148
Raises:
149
PylamaError: If any code quality issues are found
150
151
Runs pylama analysis and raises PylamaError with formatted
152
error messages if any issues are discovered.
153
"""
154
155
def repr_failure(self, excinfo, style=None):
156
"""
157
Format test failure output.
158
159
Args:
160
excinfo: Exception information from test failure
161
style: Output style (unused)
162
163
Returns:
164
str: Formatted error messages for pytest output
165
"""
166
```
167
168
### File Checking
169
170
Standalone function for checking individual files within pytest context.
171
172
```python { .api }
173
def check_file(path):
174
"""
175
Check a single file using pylama with default configuration.
176
177
Args:
178
path: File path to check
179
180
Returns:
181
List[Error]: Errors found in the file
182
183
Loads default pylama configuration and checks the specified file,
184
returning any code quality issues found.
185
"""
186
```
187
188
## Usage Examples
189
190
### Basic Pytest Integration
191
192
```python
193
# Run tests with pylama checking
194
import subprocess
195
196
# Enable pylama for all .py files
197
result = subprocess.run(['pytest', '--pylama'])
198
199
# Combine with specific test selection
200
result = subprocess.run(['pytest', '--pylama', 'tests/', '-v'])
201
202
# Use with pytest configuration
203
result = subprocess.run(['pytest', '--pylama', '--tb=short'])
204
```
205
206
### Configuration in pytest.ini
207
208
```ini
209
[tool:pytest]
210
addopts = --pylama
211
testpaths = src tests
212
python_files = *.py
213
markers =
214
pycodestyle: Code style checks
215
```
216
217
### Configuration in pyproject.toml
218
219
```toml
220
[tool.pytest.ini_options]
221
addopts = "--pylama"
222
testpaths = ["src", "tests"]
223
markers = [
224
"pycodestyle: Code style checks"
225
]
226
```
227
228
### Programmatic Usage
229
230
```python
231
import pytest
232
from pylama.pytest import check_file
233
234
# Run pytest with pylama programmatically
235
exit_code = pytest.main(['--pylama', 'src/'])
236
237
# Check individual file
238
errors = check_file('mymodule.py')
239
if errors:
240
for error in errors:
241
print(f"{error.filename}:{error.lnum} - {error.message}")
242
```
243
244
### Custom Test Integration
245
246
```python
247
import pytest
248
from pylama.pytest import check_file
249
250
def test_code_quality():
251
"""Custom test function that uses pylama checking."""
252
253
# Check specific files
254
files_to_check = ['src/main.py', 'src/utils.py']
255
256
all_errors = []
257
for filepath in files_to_check:
258
errors = check_file(filepath)
259
all_errors.extend(errors)
260
261
# Assert no code quality issues
262
if all_errors:
263
error_messages = [
264
f"{err.filename}:{err.lnum} - {err.text}"
265
for err in all_errors
266
]
267
pytest.fail(f"Code quality issues found:\n" + "\n".join(error_messages))
268
```
269
270
### Conditional Pylama Testing
271
272
```python
273
import pytest
274
import sys
275
276
# Skip pylama tests in certain conditions
277
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Requires Python 3.8+")
278
def test_pylama_advanced_features():
279
"""Test that only runs on newer Python versions."""
280
result = pytest.main(['--pylama', 'src/advanced_features.py'])
281
assert result == 0
282
```
283
284
### Integration with Coverage
285
286
```python
287
# pytest command combining pylama with coverage
288
import subprocess
289
290
result = subprocess.run([
291
'pytest',
292
'--pylama', # Code quality checks
293
'--cov=src', # Coverage reporting
294
'--cov-report=html', # HTML coverage report
295
'--cov-fail-under=80' # Minimum coverage threshold
296
])
297
```
298
299
### Custom Marker Usage
300
301
```python
302
import pytest
303
304
@pytest.mark.pycodestyle
305
def test_specific_style_check():
306
"""Test specifically marked for style checking."""
307
# This test will be collected when running with --pylama
308
pass
309
310
# Run only style-marked tests
311
# pytest -m pycodestyle --pylama
312
```
313
314
### Performance Optimization
315
316
```python
317
# pytest-xdist for parallel execution with pylama
318
import subprocess
319
320
# Run pylama checks in parallel
321
result = subprocess.run([
322
'pytest',
323
'--pylama',
324
'-n', 'auto', # Use all available CPU cores
325
'--dist=loadfile' # Distribute by file
326
])
327
```
328
329
### CI/CD Integration
330
331
```yaml
332
# GitHub Actions example
333
name: Code Quality
334
on: [push, pull_request]
335
336
jobs:
337
pylama:
338
runs-on: ubuntu-latest
339
steps:
340
- uses: actions/checkout@v2
341
- name: Set up Python
342
uses: actions/setup-python@v2
343
with:
344
python-version: 3.9
345
- name: Install dependencies
346
run: |
347
pip install pylama pytest
348
- name: Run code quality checks
349
run: pytest --pylama src/
350
```
351
352
### Custom Configuration with Pytest
353
354
```python
355
# conftest.py
356
import pytest
357
from pylama.config import parse_options
358
359
@pytest.fixture(scope="session")
360
def pylama_options():
361
"""Custom pylama configuration for tests."""
362
return parse_options([
363
'--linters=pycodestyle,pyflakes',
364
'--ignore=E501,W503',
365
'--max-line-length=100'
366
])
367
368
def test_with_custom_config(pylama_options):
369
"""Test using custom pylama configuration."""
370
from pylama.main import check_paths
371
372
errors = check_paths(['src/'], pylama_options)
373
assert len(errors) == 0, f"Code quality issues: {errors}"
374
```
375
376
## Caching System
377
378
The pytest integration includes an intelligent caching system:
379
380
```python { .api }
381
HISTKEY: str = "pylama/mtimes"
382
"""Cache key for storing file modification times."""
383
```
384
385
**Cache Behavior:**
386
- Files that pass pylama checks have their modification times cached
387
- On subsequent runs, unchanged files are skipped automatically
388
- Cache is persistent across pytest sessions
389
- Cache is invalidated when files are modified
390
- Improves performance significantly on large codebases
391
392
**Cache Location:**
393
- Uses pytest's built-in cache system (`.pytest_cache/`)
394
- Survives across different test runs and environments
395
- Can be cleared with `pytest --cache-clear`