Rattle rules for blank-line and statement-cuddling policy checks in Python.
pip install rattle-blank-linesuv add rattle-blank-linesAdd the rule pack to your project configuration:
[tool.rattle]
root = true
enable = ["rattle_blank_lines.rules"]This adds the rattle_blank_lines rules.
Run linting and autofix:
rattle lint <path>
rattle lint --diff <path>
rattle fix <path>For in-file suppressions, use Rattle comments:
# lint-ignore: BlankLineBeforeAssignment# lint-fixme: BlankLineBeforeAssignment
Removes leading and trailing blank lines at suite boundaries.
Before:
def f() -> int:
value = 1
return valueAfter:
def f() -> int:
value = 1
return valueRequires a blank line before return/raise/break/continue in larger suites.
Before:
def f(value: int) -> int:
x = value + 1
y = x + 1
z = y + 1
return zAfter:
def f(value: int) -> int:
x = value + 1
y = x + 1
z = y + 1
return zAllows cuddling before a block when the setup still belongs to the same step. The first statement after a suite docstring is exempt.
Before:
def f(value: int) -> int:
prepared = value + 1
if value > 0:
return value
return 0After:
def f(value: int) -> int:
prepared = value + 1
if value > 0:
return value
return 0Also allowed:
def f(override_name: str | None) -> str:
display_name = "guest"
if override_name is not None:
display_name = override_name
return display_namedef f(slots: dict[str, int], key: str) -> None:
slots[key] -= 1
if slots[key] < 0:
raise ValueError(key)Stricter cuddle mode. The first statement after a suite docstring is exempt.
Opt in with rattle_blank_lines.rules.block_header_cuddle_strict, and disable BlockHeaderCuddleRelaxed if you want strict mode instead of relaxed mode.
[tool.rattle]
root = true
enable = [
"rattle_blank_lines.rules",
"rattle_blank_lines.rules.block_header_cuddle_strict",
]
disable = [
"BlockHeaderCuddleRelaxed",
]Before:
def f(value: int) -> int:
header_value = value + 1
trailing = value + 2
if header_value > 0:
return header_value
return 0After:
def f(value: int) -> int:
header_value = value + 1
trailing = value + 2
if header_value > 0:
return header_value
return 0Requires a blank line after multiline control-flow blocks.
Some compact patterns stay together, such as guard ladders, with pytest.raises(...) clusters, and immediate inspection after with.
Before:
def f(value: int) -> int:
if value > 0:
value += 1
return valueAfter:
def f(value: int) -> int:
if value > 0:
value += 1
return valueRequires a blank line before an assignment after a non-assignment statement.
Before:
def f(candidate: object) -> object:
validate(candidate)
display_value = str(candidate)
if supports_live_interaction():
highlight(candidate)
return candidateUsually:
def f(candidate: object) -> object:
validate(candidate)
display_value = str(candidate)
if supports_live_interaction():
highlight(candidate)
return candidateAlso allowed:
def f() -> int:
log_start()
value = compute()
return valueAlso allowed:
def configure_logger(logger: logging.Logger, handler: logging.Handler) -> None:
logger.addHandler(handler)
logger.propagate = FalseRequires a blank line before the next case after a larger case body.
This rule is opt-in and is not included by enable = ["rattle_blank_lines.rules"].
You can enable it with enable = ["rattle_blank_lines.rules.match_case_separation"].
Before:
def f(value: int) -> int:
match value:
case 1:
a = 1
b = 2
c = 3
case _:
return 0[tool.rattle.options]
[tool.rattle.options.BlankLineBeforeBranchInLargeSuite]
max_suite_non_empty_lines = 2
compact_tail_max_statements = 2
allow_related_return_tails = true
allow_guard_ladder_final_branch = true
[tool.rattle.options.BlankLineBeforeAssignment]
short_control_flow_max_statements = 3
related_use_lookahead = 2
allow_local_helper_capture = true
allow_post_guard_continuation = true
[tool.rattle.options.BlockHeaderCuddleRelaxed]
body_usage_lookahead = 4
setup_run_lookback = 3
allow_setup_before_compact_guard_ladder = true
[tool.rattle.options.BlankLineAfterControlBlock]
related_use_lookahead = 2
allow_compact_guard_ladders = true
allow_pytest_raises_clusters = true
allow_with_immediate_inspection = true
[tool.rattle.options.MatchCaseSeparation]
max_case_non_empty_lines = 2After:
def f(value: int) -> int:
match value:
case 1:
a = 1
b = 2
c = 3
case _:
return 0