Skip to content

zigai/rattle-blank-lines

Repository files navigation

rattle-blank-lines

Tests PyPI version Supported versions Downloads license

Rattle rules for blank-line and statement-cuddling policy checks in Python.

Installation

pip install rattle-blank-lines
uv add rattle-blank-lines

Quick Start

Add 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

Rules

NoSuiteLeadingTrailingBlankLines

Removes leading and trailing blank lines at suite boundaries.

Before:

def f() -> int:

    value = 1
    return value

After:

def f() -> int:
    value = 1
    return value

BlankLineBeforeBranchInLargeSuite

Requires 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 z

After:

def f(value: int) -> int:
    x = value + 1
    y = x + 1
    z = y + 1

    return z

BlockHeaderCuddleRelaxed

Allows 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 0

After:

def f(value: int) -> int:
    prepared = value + 1

    if value > 0:
        return value

    return 0

Also allowed:

def f(override_name: str | None) -> str:
    display_name = "guest"
    if override_name is not None:
        display_name = override_name
    return display_name
def f(slots: dict[str, int], key: str) -> None:
    slots[key] -= 1
    if slots[key] < 0:
        raise ValueError(key)

BlockHeaderCuddleStrict

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 0

After:

def f(value: int) -> int:
    header_value = value + 1
    trailing = value + 2

    if header_value > 0:
        return header_value

    return 0

BlankLineAfterControlBlock

Requires 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 value

After:

def f(value: int) -> int:
    if value > 0:
        value += 1

    return value

BlankLineBeforeAssignment

Requires 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 candidate

Usually:

def f(candidate: object) -> object:
    validate(candidate)

    display_value = str(candidate)
    if supports_live_interaction():
        highlight(candidate)
    return candidate

Also allowed:

def f() -> int:
    log_start()
    value = compute()
    return value

Also allowed:

def configure_logger(logger: logging.Logger, handler: logging.Handler) -> None:
    logger.addHandler(handler)
    logger.propagate = False

MatchCaseSeparation

Requires 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

Rule Options

[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 = 2

After:

def f(value: int) -> int:
    match value:
        case 1:
            a = 1
            b = 2
            c = 3

        case _:
            return 0

License

MIT

About

Python lint rules for blank-line and statement cuddling

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors