Using Flake8 for Style Checks
What Flake8 checks
Section titled “What Flake8 checks”- style (PEP8-ish)
- unused imports
- simple logical issues
flake8 .Example configuration
Section titled “Example configuration”In .flake8:
[flake8]
max-line-length = 88
extend-ignore = E203,W503
exclude = .git,__pycache__,.venv,build,distPlugins (popular)
Section titled “Plugins (popular)”flake8-bugbearflake8-comprehensionsflake8-docstrings
If you use Black, align flake8 ignores with Black defaults.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Write a unittest TestCase
Section titled “Exercise 1 – Write a unittest TestCase”Exercise 2 – assertRaises
Section titled “Exercise 2 – assertRaises”Exercise 3 – setUp and tearDown
Section titled “Exercise 3 – setUp and tearDown”One file, six tools
Section titled “One file, six tools”Every measurement on this page — and on the other five tools in this phase — comes from running the tool against this deliberately flawed file:
import os
import subprocess
import hashlib
def process(items, user_input, flag = False):
unused_var = 42
result=[]
for i in items:
if i > 0:
if flag:
if i % 2 == 0:
result.append(i*2)
else:
result.append(i)
else:
result.append(i)
password = "hunter2"
h = hashlib.md5(password.encode()).hexdigest()
os.system("echo " + user_input)
subprocess.call("ls " + user_input, shell=True)
return result
def add(a: int, b: int) -> int:
return a + b
x = add("1", 2)| tool | what it reported on sample.py |
|---|---|
| flake8 | 5 style and dead-code issues. No security findings. |
| pylint | 4 issues, score 8.10/10 |
| mypy | 1 type error, which neither linter saw |
| bandit | 5 security issues, 3 of them HIGH |
| radon | complexity A (5), maintainability A (56.30) |
The headline is that no tool subsumes another. flake8 read the whole file and reported nothing about the shell injection on line 20. bandit read the same file and said nothing about the type error on line 29. Running one and concluding the code is clean is the mistake this phase exists to prevent.
What flake8 actually is
Section titled “What flake8 actually is”flowchart TD F["flake8"] --> P["pycodestyle
PEP 8 layout: E and W codes"] F --> Y["pyflakes
real errors: F codes"] F --> M["mccabe
complexity, off by default"] P --> R["E501 line too long, E225 missing whitespace"] Y --> R2["F841 unused variable, F401 unused import"]
flake8 is a wrapper. That matters because the codes tell you which underlying tool spoke, and how much to care:
- E / W come from
pycodestyleand are layout opinions. A formatter can fix all of them. - F come from
pyflakesand are real problems — an unused import, a name that is never used, an undefined name. These deserve attention.
Measured on sample.py:
sample.py:6:36: E251 unexpected spaces around keyword / parameter equals
sample.py:6:38: E251 unexpected spaces around keyword / parameter equals
sample.py:7:5: F841 local variable 'unused_var' is assigned to but never used
sample.py:8:11: E225 missing whitespace around operator
sample.py:19:5: F841 local variable 'h' is assigned to but never usedNote what is absent: nothing about os.system with concatenated user input on line 20,
and nothing about the type error on line 29. flake8 was never looking for either.
The line-length clash with black
Section titled “The line-length clash with black”This is the most common configuration problem in Python tooling, and it is measurable:
fmt.py:1:80: E501 line too long (84 > 79 characters)black formats to 88 columns; flake8 defaults to 79. Run both with default settings and correctly formatted code fails the linter forever. The fix is to tell flake8 the number black uses:
[flake8]
max-line-length = 88
extend-ignore = E203, W503E203 and W503 are the two other rules black deliberately violates — whitespace before
: in slices, and line breaks before binary operators. Ignoring them is the documented
combination, not a workaround.
Making it useful rather than noisy
Section titled “Making it useful rather than noisy”[flake8]
max-line-length = 88
extend-ignore = E203, W503
exclude = .git,__pycache__,build,dist,migrations,.venv
per-file-ignores =
__init__.py:F401 # re-exports are intentional there
tests/*:S101 # asserts are the point of a testper-file-ignores is what stops a team from disabling a rule globally because of two
files. Blanket # noqa on a line is the last resort — and a bare # noqa silences
everything on that line, so write # noqa: F401 with the specific code.
Check yourself
Section titled “Check yourself”-
flake8 reports E, W and F codes. What is the difference?
flake8 is a wrapper around pycodestyle, pyflakes and mccabe. The prefix tells you which tool spoke and how much the finding is worth.
pch.quizShowAnswer
B — E and W come from pycodestyle and are layout opinions a formatter can fix; F comes from pyflakes and marks real problems such as unused or undefined names — flake8 is a wrapper around pycodestyle, pyflakes and mccabe. The prefix tells you which tool spoke and how much the finding is worth.
-
Code formatted by black fails flake8 with E501 line too long. Why?
Measured 84 > 79 on a black-formatted line. Set max-line-length = 88 and extend-ignore = E203, W503, which are the rules black deliberately violates.
pch.quizShowAnswer
B — black formats to 88 columns while flake8 defaults to 79, so the two disagree until flake8 is configured — Measured 84 > 79 on a black-formatted line. Set max-line-length = 88 and extend-ignore = E203, W503, which are the rules black deliberately violates.
-
What did flake8 report about the os.system call built from user input in the sample?
flake8 read the line and had nothing to say. Bandit flagged the same line B605 HIGH. A clean flake8 run says the layout is consistent, nothing more.
pch.quizShowAnswer
B — nothing at all; it does not look for security issues — flake8 read the line and had nothing to say. Bandit flagged the same line B605 HIGH. A clean flake8 run says the layout is consistent, nothing more.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading