pytest plugin to check source code with pyflakes
npx @tessl/cli install tessl/pypi-pytest-flakes@4.0.0A 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.
pip install pytest-flakes# Plugin is automatically loaded by pytest when installed
# No explicit imports needed for command line usageFor programmatic access to internal components:
from pytest_flakes import (
FlakesPlugin, FlakesError, FlakesFile, FlakesItem,
Ignorer, check_file, is_ignored_line,
PYTEST_GTE_7, HISTKEY, assignment_monkeypatched_init
)Required external imports for the module:
import re
import pathlib
import pytest
import sys
import tokenize
import _ast
from pyflakes.checker import Binding, Assignment, Checker
from pyflakes.api import isPythonFile# Run pytest with pyflakes checking
pytest --flakes
# Run only pyflakes tests (pytest 2.4+)
pytest --flakes -m flakes
# Run only pyflakes tests (pytest < 2.4)
pytest --flakes -k flakesAdd to setup.cfg or pytest.ini:
[pytest]
# Ignore specific error types globally
flakes-ignore = ImportStarUsed
# Ignore errors per file with glob patterns
flakes-ignore =
*.py UnusedImport
doc/conf.py ALL
tests/*.py UnusedImport UnusedVariableimport sys # noqa
import os # pragma: no flakesConstant to determine pytest version compatibility.
PYTEST_GTE_7 = hasattr(pytest, 'version_tuple') and pytest.version_tuple >= (7, 0)Constant defining the cache key for storing file modification times.
HISTKEY = "flakes/mtimes"Internal monkey patch for pyflakes Assignment class to handle __tracebackhide__.
def assignment_monkeypatched_init(self, name: str, source):
"""
Monkey patch for pyflakes Assignment.__init__ to mark __tracebackhide__ as used.
Args:
self: Assignment instance
name (str): variable name
source: source information from pyflakes
Returns:
None
"""The plugin automatically registers with pytest through the entry point system.
# Entry point in setup.py
entry_points={'pytest11': ['flakes = pytest_flakes']}Adds the --flakes option to pytest and configuration settings.
def pytest_addoption(parser):
"""
Add command line options for pyflakes checking.
Args:
parser: pytest argument parser instance
Returns:
None
"""Configures the plugin when the --flakes option is used.
def pytest_configure(config):
"""
Configure the pyflakes plugin and register it with pytest.
Args:
config: pytest configuration object
Returns:
None
"""The main plugin class that handles file discovery and pyflakes integration.
class FlakesPlugin:
def __init__(self, config):
"""
Initialize plugin with configuration settings.
Args:
config: pytest configuration object
Returns:
None
"""
def pytest_collect_file(self, file_path, parent):
"""
Collect Python files for pyflakes checking (pytest >= 7).
Args:
file_path: pathlib.Path to the file
parent: pytest parent node
Returns:
FlakesFile: instance if file should be checked, None otherwise
"""
def pytest_collect_file(self, path, parent):
"""
Collect Python files for pyflakes checking (pytest < 7).
Args:
path: py.path.local object to the file
parent: pytest parent node
Returns:
FlakesFile: instance if file should be checked, None otherwise
"""
def pytest_sessionfinish(self, session):
"""
Save cache data after test session completes.
Args:
session: pytest session object
Returns:
None
"""Represents a Python file to be checked by pyflakes.
class FlakesFile(pytest.File):
def __init__(self, *k, flakesignore, **kw):
"""
Initialize file with ignore settings.
Args:
*k: positional arguments passed to parent
flakesignore (list): error types to ignore for this file
**kw: keyword arguments passed to parent
Returns:
None
"""
def collect(self):
"""
Create test item for this file.
Returns:
list: containing FlakesItem instance with name "flake-8"
"""Test item that performs the actual pyflakes checking.
class FlakesItem(pytest.Item):
def __init__(self, *k, **kw):
"""
Initialize test item and add flakes marker.
Args:
*k: positional arguments passed to parent
**kw: keyword arguments passed to parent
Returns:
None
"""
def setup(self):
"""
Check file modification time and skip if unchanged.
Returns:
None
"""
def runtest(self):
"""
Execute pyflakes check on the file.
Returns:
None
Raises:
FlakesError: if pyflakes finds any issues
"""
def repr_failure(self, excinfo):
"""
Format failure output for pyflakes errors.
Args:
excinfo: exception information
Returns:
str: formatted error message
"""
def reportinfo(self):
"""
Return test report information.
Returns:
tuple: (file_path, line_number, test_name)
"""Exception raised when pyflakes checks fail.
class FlakesError(Exception):
"""Indicates an error during pyflakes checks."""Handles ignore patterns for files and error types.
class Ignorer:
def __init__(self, ignorelines, coderex=re.compile(r"[EW]\d\d\d")):
"""
Initialize with ignore pattern configuration.
Args:
ignorelines (list): ignore pattern lines from config
coderex (re.Pattern): regex for matching error codes (default handles E/W codes)
Returns:
None
"""
def __call__(self, path):
"""
Get ignore list for a specific file path.
Args:
path (pathlib.Path): file path to check
Returns:
list: error types to ignore for this file, or None to ignore all
"""Core function that runs pyflakes analysis on a file.
def check_file(path, flakesignore):
"""
Run pyflakes check on a Python file.
Args:
path (pathlib.Path): file to check
flakesignore (list): error types to ignore
Returns:
tuple: (error_count: int, error_messages: list)
"""Utility function to check for inline ignore comments.
def is_ignored_line(line):
"""
Check if a line should be ignored based on comments.
Args:
line (str): line of code to check
Returns:
bool: True if line ends with '# noqa' or '# pragma: no flakes'
"""--flakes: Enable pyflakes checking for all discovered Python filesAdd to [pytest] section in setup.cfg or pytest.ini:
flakes-ignore: Specify patterns and error types to ignore# Global ignore (applies to all files)
flakes-ignore = ImportStarUsed UnusedImport
# File-specific ignores using glob patterns
flakes-ignore =
*.py UnusedImport
tests/*.py UnusedVariable
doc/conf.py ALLUnusedImport, UnusedVariable, ImportStarUsed, UndefinedName, etc.*.py, tests/*.py, doc/conf.py, etc.# noqa: Ignore all pyflakes errors on this line# pragma: no flakes: Alternative ignore comment formatThe plugin automatically adds the flakes marker to all pyflakes test items, allowing selective test execution:
# Run only pyflakes tests
pytest -m flakes
# Run everything except pyflakes tests
pytest -m "not flakes"The plugin uses pytest's built-in caching mechanism to avoid re-analyzing unchanged files. Cache key is based on:
Cache data is stored under the key "flakes/mtimes" and persists between test runs for improved performance.
Common pyflakes error types that can be detected and ignored:
UnusedImport: Imported modules that are never usedUnusedVariable: Variables that are assigned but never usedImportStarUsed: Star imports that make undefined name detection impossibleUndefinedName: References to undefined variables or functionsRedefinedWhileUnused: Variables redefined before being usedImportShadowedByLoopVar: Imports shadowed by loop variablespyflakes.api.isPythonFile()tokenize.open()