python-pep8 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited python-pep8 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A summary of PEP 8 with practical do/don't examples. Ruff/black handle formatting automatically — this guide is about the semantic decisions auto-formatters do not make.
4 spaces per level. No tabs in new code.
# OK
def f(
a, b, c,
d):
print(a)
# OK (vertical alignment)
result = some_fn(arg1, arg2,
arg3, arg4)# Bad (too long)
result = some_very_long_function_name_that_exceeds_the_character_limit(a, b, c)
# Good
result = some_very_long_function_name_that_exceeds_the_character_limit(
a, b, c,
)class A:
def m1(self): ...
def m2(self): ...
def standalone():
...Break before the operator, not after (mathematical convention, reads more clearly).
# Good
income = (gross_wages
+ taxable_interest
- ira_deduction)
# Bad
income = (gross_wages +
taxable_interest -
ira_deduction)UTF-8 without BOM. No # -*- coding: utf-8 -*- in Python 3 (it is already the default).
Groups (with a blank line between):
import os
import sys
from datetime import datetime
import flask
import peewee
from models import User
from serializers import user_serializerimport os + import sys, not import os, sys.from myapp.utils import x, not from .utils import x (except in explicit package cases).from x import *.| Entity | Style | Example |
|---|---|---|
| Module / package | snake_case | data_processor.py |
| Class | PascalCase | UserAccount, HTTPServer |
| Exception | PascalCase + Error | ValidationError |
| Function, method | snake_case | calculate_total |
| Variable | snake_case | user_count |
| Constant | UPPER_CASE | MAX_CONNECTIONS = 100 |
| Type variable | short PascalCase | T, KT, VT |
Special notes:
self for instance methods, cls for classmethod. Not this, not obj.class_, type_ (trailing underscore)._ — non-public (by convention, not enforced).__ — name mangling (only meaningful in inheritance).__name__ — dunder, do not define your own.# Bad
class user_account: pass # should be PascalCase
def CalcTotal(): pass # should be snake_case
maxConnections = 100 # should be UPPER_CASE
def m(this, x): pass # should be self# Good
spam(ham[1], {eggs: 2})
if x == 4:
print(x)
# Bad
spam( ham[ 1 ], { eggs: 2 } )
if x == 4 :
...One space around. With mixed precedence, you may omit spaces around higher-precedence operators.
# Good
i = i + 1
hypot2 = x*x + y*y # acceptable: multiplication has higher precedence
# Bad
i=i+1
x = x*2-1 # inconsistentNo spaces around = for default parameter values and kwargs.
# Good
def fn(real, imag=0.0):
return magic(r=real, i=imag)
# Bad
def fn(real, imag = 0.0):
return magic(r = real, i = imag)But when there is a type annotation, spaces around = are present:
# Good
def fn(real: float, imag: float = 0.0): ...
# Bad
def fn(real: float, imag: float=0.0): ...Space after, not before.
# Good
if x == 4: print(x, y)
foo = (0,)
# Bad
if x == 4 : print(x , y)
foo = (0, )# + one space. Full sentences, capital letter. Indentation level matches the code being commented on.
# Apply tax at the regional rate.
# Round to two decimal places — compliance requirement.
total = base * (1 + tax_rate)At least 2 spaces before #, then # . Use sparingly — only when explaining why, not what.
x = x + 1 # Compensate for boundary offset"""...""", no blank lines.""" on its own line.def fetch_data():
"""Return processed data."""
def long_one():
"""Short summary.
Longer description with details and examples.
"""is / is not# Good
if x is None: ...
if x is not None: ...
# Bad
if x == None: ...
if not x is None: ... # reads ambiguously# Good
if greeting:
...
# Bad
if greeting == True: ...
if greeting is True: ... # has cases, but usually redundant# Good
if not items: ...
if items: ...
# Bad
if len(items) == 0: ...
if len(items) > 0: ...# Good
try:
import platform
except ImportError:
platform = None
# Bad
try:
...
except: # also catches KeyboardInterrupt, SystemExit
passexcept Exception: is acceptable when you really do catch everything and log it. except: — almost never.
Either every branch returns a value, or none do. Explicit return None.
# Good
def f(x):
if x >= 0:
return math.sqrt(x)
return None
# Bad
def f(x):
if x >= 0:
return math.sqrt(x)
# implicit return None — unclear to the reader whether intentional# Good
if filename.endswith(".txt"): ...
if name.startswith("test_"): ...
# Bad
if filename[-4:] == ".txt": ...# Good
if isinstance(x, int): ...
if isinstance(x, (int, float)): ...
# Bad
if type(x) is int: ...
if type(x) == int: ...Don't accumulate via += in a loop — O(n²). "".join(...) is linear.
# Good
result = "".join(parts)
# Bad
result = ""
for p in parts:
result += plambda in def# Good
def square(x):
return x * x
# Bad
square = lambda x: x * x# Good
with open(path) as f:
data = f.read()
# Bad
f = open(path)
try:
data = f.read()
finally:
f.close()PEP 8 is the baseline. Modern rules ruff enables by default:
list[int] instead of List[int] (Python 3.9+).int | None instead of Optional[int] (Python 3.10+).% and .format().match/case instead of long if/elif by type.pathlib.Path instead of os.path.join.Manual PEP 8 → 0. Use:
ruff check . # linting (replaces flake8/pylint/isort/...)
ruff format . # formatting (replaces black)
mypy . # typesruff is the only linter you need in 2025+. Configure in pyproject.toml:
[tool.ruff]
line-length = 88
target-version = "py312"
[tool.ruff.lint]
select = [
"E", "W", # pycodestyle (PEP 8)
"F", # pyflakes
"I", # isort
"B", # bugbear
"UP", # pyupgrade
"SIM", # simplify
"C4", # comprehensions
"N", # pep8-naming
]Layout:
- [ ] 4-space indent, no tabs
- [ ] Lines ≤ 88 characters (docstrings ≤ 72)
- [ ] 2 blank lines between top-level def/class
- [ ] Line break BEFORE the binary operator
Imports:
- [ ] Groups: stdlib / third-party / local
- [ ] One import per line
- [ ] No `from x import *`
Names:
- [ ] Class — PascalCase
- [ ] Function/variable — snake_case
- [ ] Constant — UPPER_CASE
Programming:
- [ ] `is None` / `is not None` (not `== None`)
- [ ] Specific `except`, not bare
- [ ] `if not items` (not `len(items) == 0`)
- [ ] `isinstance(x, T)` (not `type(x) is T`)
- [ ] `"".join(parts)` for concatenation in a loop
- [ ] `with open(...)` for resources
- [ ] `def`, not `name = lambda`
Tooling:
- [ ] `ruff check .` passes
- [ ] `ruff format .` applied
- [ ] `mypy .` passes (in strict, if possible)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.