| name | flake8 |
| description | [Applies to: **/*.py] This guide provides definitive, actionable best practices for configuring and using flake8 to enforce Python code quality, style, and catch common errors. |
| source | cursor_mdc |
flake8 Best Practices
flake8 is your essential Python linter, combining pycodestyle, pyflakes, and mccabe complexity checks with a powerful plugin ecosystem. It ensures your codebase adheres to PEP 8, catches common bugs, and maintains high quality. This guide outlines the definitive approach to using flake8 effectively.
1. Configuration: The Single Source of Truth
Always configure flake8 at the repository root using pyproject.toml. This centralizes tooling configuration and ensures consistent behavior across all environments. Avoid .flake8 or setup.cfg for new projects.
✅ GOOD: pyproject.toml
[tool.flake8]
max-line-length = 88
extend-ignore = [
"E203",
"W503",
"E501",
]
exclude = [
".git",
"__pycache__",
"build",
"dist",
"venv",
".venv",
"*.egg-info",
"docs",
"migrations",
]
max-complexity = 10
per-file-ignores = [
"src/legacy_module.py:E501,F401",
"tests/*:S101",
]
enable-extensions = ["B", "B9", "S", "M"]
❌ BAD: .flake8 (Outdated)
[flake8]
max-line-length = 88
ignore = E203,W503
exclude = .git,__pycache__,build,dist,venv,.venv,*.egg-info,docs,migrations
max-complexity = 10
2. Core Checks: What flake8 Enforces
flake8 combines pycodestyle (PEP 8 style), pyflakes (common bugs), and mccabe (cyclomatic complexity).
a. pycodestyle (E/W codes) - Style Enforcement
E501: Line Too Long
Keep lines concise for readability. Pair flake8 with black or ruff format for automatic formatting.
❌ BAD
def calculate_complex_value(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10):
return (param1 + param2) * (param3 - param4) / (param5 + param6) - (param7 * param8) + param9 - param10
✅ GOOD
def calculate_complex_value(
param1, param2, param3, param4, param5,
param6, param7, param8, param9, param10
):
intermediate_sum = (param1 + param2) * (param3 - param4)
intermediate_diff = (param5 + param6) - (param7 * param8)
return intermediate_sum / intermediate_diff + param9 - param10
b. pyflakes (F codes) - Bug Detection
F401: Unused Import
Remove unused imports to keep your code clean and prevent circular dependencies.
❌ BAD
import os
import sys
def greet(name):
print(f"Hello, {name}!")
✅ GOOD
import sys
def greet(name):
print(f"Hello, {name}!")
c. mccabe (C901) - Cyclomatic Complexity
High cyclomatic complexity indicates a function is doing too much. Refactor complex functions into smaller, focused units. Aim for max-complexity = 10.
❌ BAD
def process_data(data):
if data is None:
return None
if not isinstance(data, list):
return []
processed = []
for item in data:
if isinstance(item, dict):
if "value" in item and item["value"] > 0:
processed.append(item["value"] * 2)
elif "default" in item:
processed.append(item["default"])
elif isinstance(item, int) and item % 2 == 0:
processed.append(item / 2)
else:
processed.append(item)
return processed
✅ GOOD
def _process_dict_item(item):
if "value" in item and item["value"] > 0:
return item["value"] * 2
if "default" in item:
return item["default"]
return None
def _process_int_item(item):
if item % 2 == 0:
return item / 2
return item
def process_data(data):
if data is None:
return None
if not isinstance(data, list):
return []
processed = []
for item in data:
if isinstance(item, dict):
result = _process_dict_item(item)
if result is not None:
processed.append(result)
elif (item, ):
processed.append(_process_int_item(item))
:
processed.append(item)
processed
3. Essential Plugins: Extend flake8's Power
Always install and enable these plugins for a robust linting setup.
flake8-bandit (S codes): Security linter. Catches common security issues.
flake8-bugbear (B/B9 codes): Catches common "bear" bugs and design problems.
flake8-mypy (M codes): Integrates mypy type checking into flake8 output.
Installation:
pip install flake8 flake8-bandit flake8-bugbear flake8-mypy
Configuration (already in pyproject.toml example):
[tool.flake8]
enable-extensions = ["B", "B9", "S", "M"]
Example: flake8-bandit (S101 - assert statement)
❌ BAD
def check_password(password):
assert password is not None, "Password cannot be None"
✅ GOOD
def check_password(password):
if password is None:
raise ValueError("Password cannot be None")
4. Workflow Integration: Lint Early, Lint Often
Integrate flake8 into your development workflow to catch issues as early as possible.
a. Local Development (IDE Integration)
Configure your IDE (e.g., Cursor, VS Code) to run flake8 on file save. This provides immediate feedback.
b. Pre-commit Hooks
Use pre-commit to automatically run flake8 (and formatters like black or ruff format) before every commit. This ensures no unlinted code ever reaches your repository.
pyproject.toml (or .pre-commit-config.yaml)
repos:
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
args: ["--config=pyproject.toml"]
c. CI/CD Pipelines
Include flake8 as a mandatory step in your CI pipeline. If flake8 reports any errors, the build must fail. This acts as a quality gate.
5. Ignoring Errors: Be Specific, Not Lazy
Only ignore errors when absolutely necessary, and always be specific.
❌ BAD: Blanket Ignore
import os, sys
✅ GOOD: Specific Ignore
import os
import sys
✅ GOOD: File-level Ignore (in pyproject.toml)
[tool.flake8]
per-file-ignores = [
"src/legacy_module.py:E501,F401",
]
6. Type Hints: Leverage flake8-mypy
While mypy is the primary type checker, flake8-mypy integrates type checking results into your flake8 output, providing a unified view of code quality.
❌ BAD
def add(a, b):
return a + b
✅ GOOD
def add(a: int, b: int) -> int:
return a + b
7. Virtual Environments: Isolate Dependencies
Always install flake8 and its plugins within a project-specific virtual environment. This prevents dependency conflicts and ensures consistent behavior.
python -m venv .venv
source .venv/bin/activate
pip install flake8 flake8-bandit flake8-bugbear flake8-mypy
8. Code Organization & Structure
flake8 indirectly encourages good structure by enforcing PEP 8 and catching issues.
- Separate Concerns: Run
flake8 on src/ and tests/ directories. Exclude docs/ or migrations/ as needed.
- Import Order: While
flake8 itself doesn't enforce strict import order, isort (or ruff format) does. Pair flake8 with isort for this.
- Standard library
- Third-party
- Local application
- Avoid Circular Dependencies:
pyflakes (part of flake8) will warn about these.
9. Common Pitfalls & Gotchas
- Outdated Configuration: Always use
pyproject.toml.
- Ignoring too much: A long
ignore list hides real issues. Fix the code, don't ignore the linter.
- Not running
flake8 everywhere: Ensure flake8 runs on all relevant Python files (src, tests).
- Mixing
flake8 with black without E203, W503 ignores: This will lead to conflicts. Always configure flake8 for black compatibility.
By following these guidelines, your team will maintain a consistent, high-quality Python codebase, reducing technical debt and improving collaboration.