pytest plugin to check source code with pyflakes
npx @tessl/cli install tessl/pypi-pytest-flakes@4.0.00
# pytest-flakes
1
2
A pytest plugin that integrates pyflakes static analysis into the pytest testing framework. It enables automatic checking of Python source code for common issues like unused imports, undefined variables, and import star usage during test execution.
3
4
## Package Information
5
6
- **Package Name**: pytest-flakes
7
- **Package Type**: pypi
8
- **Language**: Python
9
- **Installation**: `pip install pytest-flakes`
10
- **Dependencies**: pytest>=5, pyflakes
11
12
## Core Imports
13
14
```python
15
# Plugin is automatically loaded by pytest when installed
16
# No explicit imports needed for command line usage
17
```
18
19
For programmatic access to internal components:
20
21
```python
22
from pytest_flakes import (
23
FlakesPlugin, FlakesError, FlakesFile, FlakesItem,
24
Ignorer, check_file, is_ignored_line,
25
PYTEST_GTE_7, HISTKEY, assignment_monkeypatched_init
26
)
27
```
28
29
Required external imports for the module:
30
31
```python
32
import re
33
import pathlib
34
import pytest
35
import sys
36
import tokenize
37
import _ast
38
from pyflakes.checker import Binding, Assignment, Checker
39
from pyflakes.api import isPythonFile
40
```
41
42
## Basic Usage
43
44
### Command Line Usage
45
46
```python
47
# Run pytest with pyflakes checking
48
pytest --flakes
49
50
# Run only pyflakes tests (pytest 2.4+)
51
pytest --flakes -m flakes
52
53
# Run only pyflakes tests (pytest < 2.4)
54
pytest --flakes -k flakes
55
```
56
57
### Configuration
58
59
Add to `setup.cfg` or `pytest.ini`:
60
61
```ini
62
[pytest]
63
# Ignore specific error types globally
64
flakes-ignore = ImportStarUsed
65
66
# Ignore errors per file with glob patterns
67
flakes-ignore =
68
*.py UnusedImport
69
doc/conf.py ALL
70
tests/*.py UnusedImport UnusedVariable
71
```
72
73
### Inline Ignoring
74
75
```python
76
import sys # noqa
77
import os # pragma: no flakes
78
```
79
80
## Constants and Internal Functions
81
82
### Version Compatibility
83
84
Constant to determine pytest version compatibility.
85
86
```python { .api }
87
PYTEST_GTE_7 = hasattr(pytest, 'version_tuple') and pytest.version_tuple >= (7, 0)
88
```
89
90
### Cache Key
91
92
Constant defining the cache key for storing file modification times.
93
94
```python { .api }
95
HISTKEY = "flakes/mtimes"
96
```
97
98
### Monkey Patching
99
100
Internal monkey patch for pyflakes Assignment class to handle `__tracebackhide__`.
101
102
```python { .api }
103
def assignment_monkeypatched_init(self, name: str, source):
104
"""
105
Monkey patch for pyflakes Assignment.__init__ to mark __tracebackhide__ as used.
106
107
Args:
108
self: Assignment instance
109
name (str): variable name
110
source: source information from pyflakes
111
112
Returns:
113
None
114
"""
115
```
116
117
## Capabilities
118
119
### Plugin Registration
120
121
The plugin automatically registers with pytest through the entry point system.
122
123
```python { .api }
124
# Entry point in setup.py
125
entry_points={'pytest11': ['flakes = pytest_flakes']}
126
```
127
128
### Command Line Options
129
130
Adds the `--flakes` option to pytest and configuration settings.
131
132
```python { .api }
133
def pytest_addoption(parser):
134
"""
135
Add command line options for pyflakes checking.
136
137
Args:
138
parser: pytest argument parser instance
139
140
Returns:
141
None
142
"""
143
```
144
145
### Plugin Configuration
146
147
Configures the plugin when the `--flakes` option is used.
148
149
```python { .api }
150
def pytest_configure(config):
151
"""
152
Configure the pyflakes plugin and register it with pytest.
153
154
Args:
155
config: pytest configuration object
156
157
Returns:
158
None
159
"""
160
```
161
162
### File Collection and Checking
163
164
The main plugin class that handles file discovery and pyflakes integration.
165
166
```python { .api }
167
class FlakesPlugin:
168
def __init__(self, config):
169
"""
170
Initialize plugin with configuration settings.
171
172
Args:
173
config: pytest configuration object
174
175
Returns:
176
None
177
"""
178
179
def pytest_collect_file(self, file_path, parent):
180
"""
181
Collect Python files for pyflakes checking (pytest >= 7).
182
183
Args:
184
file_path: pathlib.Path to the file
185
parent: pytest parent node
186
187
Returns:
188
FlakesFile: instance if file should be checked, None otherwise
189
"""
190
191
def pytest_collect_file(self, path, parent):
192
"""
193
Collect Python files for pyflakes checking (pytest < 7).
194
195
Args:
196
path: py.path.local object to the file
197
parent: pytest parent node
198
199
Returns:
200
FlakesFile: instance if file should be checked, None otherwise
201
"""
202
203
def pytest_sessionfinish(self, session):
204
"""
205
Save cache data after test session completes.
206
207
Args:
208
session: pytest session object
209
210
Returns:
211
None
212
"""
213
```
214
215
### File Representation
216
217
Represents a Python file to be checked by pyflakes.
218
219
```python { .api }
220
class FlakesFile(pytest.File):
221
def __init__(self, *k, flakesignore, **kw):
222
"""
223
Initialize file with ignore settings.
224
225
Args:
226
*k: positional arguments passed to parent
227
flakesignore (list): error types to ignore for this file
228
**kw: keyword arguments passed to parent
229
230
Returns:
231
None
232
"""
233
234
def collect(self):
235
"""
236
Create test item for this file.
237
238
Returns:
239
list: containing FlakesItem instance with name "flake-8"
240
"""
241
```
242
243
### Test Item Execution
244
245
Test item that performs the actual pyflakes checking.
246
247
```python { .api }
248
class FlakesItem(pytest.Item):
249
def __init__(self, *k, **kw):
250
"""
251
Initialize test item and add flakes marker.
252
253
Args:
254
*k: positional arguments passed to parent
255
**kw: keyword arguments passed to parent
256
257
Returns:
258
None
259
"""
260
261
def setup(self):
262
"""
263
Check file modification time and skip if unchanged.
264
265
Returns:
266
None
267
"""
268
269
def runtest(self):
270
"""
271
Execute pyflakes check on the file.
272
273
Returns:
274
None
275
276
Raises:
277
FlakesError: if pyflakes finds any issues
278
"""
279
280
def repr_failure(self, excinfo):
281
"""
282
Format failure output for pyflakes errors.
283
284
Args:
285
excinfo: exception information
286
287
Returns:
288
str: formatted error message
289
"""
290
291
def reportinfo(self):
292
"""
293
Return test report information.
294
295
Returns:
296
tuple: (file_path, line_number, test_name)
297
"""
298
```
299
300
### Error Handling
301
302
Exception raised when pyflakes checks fail.
303
304
```python { .api }
305
class FlakesError(Exception):
306
"""Indicates an error during pyflakes checks."""
307
```
308
309
### Ignore Pattern Processing
310
311
Handles ignore patterns for files and error types.
312
313
```python { .api }
314
class Ignorer:
315
def __init__(self, ignorelines, coderex=re.compile(r"[EW]\d\d\d")):
316
"""
317
Initialize with ignore pattern configuration.
318
319
Args:
320
ignorelines (list): ignore pattern lines from config
321
coderex (re.Pattern): regex for matching error codes (default handles E/W codes)
322
323
Returns:
324
None
325
"""
326
327
def __call__(self, path):
328
"""
329
Get ignore list for a specific file path.
330
331
Args:
332
path (pathlib.Path): file path to check
333
334
Returns:
335
list: error types to ignore for this file, or None to ignore all
336
"""
337
```
338
339
### File Checking
340
341
Core function that runs pyflakes analysis on a file.
342
343
```python { .api }
344
def check_file(path, flakesignore):
345
"""
346
Run pyflakes check on a Python file.
347
348
Args:
349
path (pathlib.Path): file to check
350
flakesignore (list): error types to ignore
351
352
Returns:
353
tuple: (error_count: int, error_messages: list)
354
"""
355
```
356
357
### Line-level Ignore Detection
358
359
Utility function to check for inline ignore comments.
360
361
```python { .api }
362
def is_ignored_line(line):
363
"""
364
Check if a line should be ignored based on comments.
365
366
Args:
367
line (str): line of code to check
368
369
Returns:
370
bool: True if line ends with '# noqa' or '# pragma: no flakes'
371
"""
372
```
373
374
## Configuration Options
375
376
### Command Line Options
377
378
- `--flakes`: Enable pyflakes checking for all discovered Python files
379
380
### Configuration File Settings
381
382
Add to `[pytest]` section in `setup.cfg` or `pytest.ini`:
383
384
- `flakes-ignore`: Specify patterns and error types to ignore
385
386
#### Ignore Pattern Format
387
388
```ini
389
# Global ignore (applies to all files)
390
flakes-ignore = ImportStarUsed UnusedImport
391
392
# File-specific ignores using glob patterns
393
flakes-ignore =
394
*.py UnusedImport
395
tests/*.py UnusedVariable
396
doc/conf.py ALL
397
```
398
399
#### Supported Ignore Patterns
400
401
- **Error types**: `UnusedImport`, `UnusedVariable`, `ImportStarUsed`, `UndefinedName`, etc.
402
- **ALL**: Ignore all errors for matching files
403
- **Glob patterns**: `*.py`, `tests/*.py`, `doc/conf.py`, etc.
404
405
### Inline Ignore Comments
406
407
- `# noqa`: Ignore all pyflakes errors on this line
408
- `# pragma: no flakes`: Alternative ignore comment format
409
410
## Test Markers
411
412
The plugin automatically adds the `flakes` marker to all pyflakes test items, allowing selective test execution:
413
414
```bash
415
# Run only pyflakes tests
416
pytest -m flakes
417
418
# Run everything except pyflakes tests
419
pytest -m "not flakes"
420
```
421
422
## Caching
423
424
The plugin uses pytest's built-in caching mechanism to avoid re-analyzing unchanged files. Cache key is based on:
425
426
- File modification time
427
- Current ignore settings for the file
428
429
Cache data is stored under the key `"flakes/mtimes"` and persists between test runs for improved performance.
430
431
## Error Types
432
433
Common pyflakes error types that can be detected and ignored:
434
435
- `UnusedImport`: Imported modules that are never used
436
- `UnusedVariable`: Variables that are assigned but never used
437
- `ImportStarUsed`: Star imports that make undefined name detection impossible
438
- `UndefinedName`: References to undefined variables or functions
439
- `RedefinedWhileUnused`: Variables redefined before being used
440
- `ImportShadowedByLoopVar`: Imports shadowed by loop variables
441
442
## Integration Notes
443
444
- Automatically discovers Python files using `pyflakes.api.isPythonFile()`
445
- Supports both pytest >= 7.0 and earlier versions with compatibility layer
446
- Handles file encoding properly using `tokenize.open()`
447
- Works with pytest's test collection and reporting mechanisms
448
- Integrates with pytest's caching system for performance optimization