Skip to main content

PyPI version License Python versions

Blanken

Blank Line Enforcement for Python

Overview

Blanken is a Python code auto-formatter that enforces separation of logical code blocks via proper usage of blank lines.

It uses indentation changes and keywords as the primary way to know where logical blocks start or end, and inserts blank lines to separate them if they are missing.

Usage

Install with pip

pip install blanken

Run on one or more files to insert blank lines where required:

blanken path/to/file.py

Use from Python:

from blanken import enforce

enforce(["path/to/file.py"])

Pre-commit

Add the hook to your .pre-commit-config.yaml:

repos:
- repo: https://github.com/csala/blanken
  rev: v0.1.0
  hooks:
  - id: blanken

This hook will auto-format the files and fail if any file has been modified by blanken.

Philosophy

The idea behind Blanken is to guarantee that source code is easy to read and understand at first glance with as little mental effort as possible. And we, the blanken authors, believe that blank lines play a super important role in this "first glance" understanding: by using them to properly separate logically unrelated blocks of code, we help the brain quickly understand the context in which the code that is being read operates while keeping it isolated from other unrelated blocks.

Identifying which lines of code are related or unrelated with simple rules that do not leverage semantic understanding of the code is really hard. For this reason, blanken only enforces a few simple rules based on keywords and indented blocks: We understand that when indentation is reduced, the lines that follow are logically unrelated to the previous ones, with the exception of some special continuation blocks like else or elif, where the lines that follow may need to be kept in consideration to fully understand the previous ones.

Enforced rules

Dedent by 2+ levels requires one blank line

If we find a line that is has 2 or more indentation levels than the previous one, we know for sure that the lines that follow belong to a new logical block, because it cannot be a continuation block.

Therefore, we enforce a blank line.

Bad:

if outer():
    if inner():
        do_inner()
do_next()

Corrected:

if outer():
    if inner():
        do_inner()

do_next()

Dedent by 1 level requires one blank line if no continuation keyword is found.

If we find a line that has 1 indentation level less than the previous one, we consider that it is unrelated to the previous one if it does not start with any of the following continuation keywords:

  • else
  • elif
  • except
  • finally

Good:

if something:
    do_something()
elif something_else:
    do_something_else()
else:
    do_another_thing()

try:
    something_dangerous()
except SomeException:
    log_the_error()
finally:
    clean_things_up()

Bad:

for item in items:
    handle(item)
while some_condition:
    run_some_logic()
log_done()

Corrected:

for item in items:
    handle(item)

while some_condition:
    run_some_logic()

log_done()

Dedent by 1 level requires one blank line if block is too long

If we find a line that has 1 indentation level less than the previous one and is followed by one of the continuation keywords listed above, we do not require a blank line if the block is short.

If the indented block is too long or complex, we understand that the block will need to be read and understood on its own, and that it is better to separate it visually from the one that follows, even if the next one starts with one of the continuation lines.

In particular, if the indented block has more than 3 top-level statements, we require a blank line.

Bad:

if condition:
    one()
    two()
    three()
    four()
else:
    five()

Corrected:

if condition:
    one()
    two()
    three()
    four()

else:
    five()

The example above seems probably simple, but consider the next one. Bear in mind that only top-level statements are counted:

Bad:

if condition:
    one()
    if two:
        nested_one()
        nested_two()
    else:
        nested_three()

    for item in three:
        nested_four()
        for nested_five:
            nested_six()
            nested_seven()

    four()
else:
    five()

In a situation like the one above, one could easily be tricked into missing the four() statement, since visually it falls closer than the five() call that is unrelated.

So we enforce a blank line:

Corrected:

if condition:
    one()
    if two:
        nested_one()
        nested_two()
    else:
        nested_three()

    for item in three:
        nested_four()
        for nested_five:
            nested_six()
            nested_seven()

    four()

else:
    five()

Development Roadmap

  • Standalone script to run on individual files
  • Installable package and cli tool to run on individual files
  • Pre-commit hook
  • Run recursively on folders
  • Separate validation from file formatting
  • Add discovery CLI options (such as exclude or include)
  • Read options from pyproject
  • Add formatting options

Metadata

Release files for blanken 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for blanken 0.1.2
File Size Uploaded
blanken-0.1.2.tar.gz 26.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for blanken 0.1.2
File Interpreter ABI Platform
blanken-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 34.6 kB

Release files / blanken-0.1.2.tar.gz

Download URL blanken-0.1.2.tar.gz
Size 26.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7f6c603225278f342aea21766535178f599309e3d70f8a160c4350a3de21b70a
BLAKE2b-256 checksum
How to use checksums
ca21a954fb85a0fe896495442f903c4967e27691e7d8b51dc0f7f86e2c145929
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / blanken-0.1.2-py3-none-any.whl

Download URL blanken-0.1.2-py3-none-any.whl
Size 8.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5018bc43c6f6d2bc8ef1118ddb4fdd4a43bb51837bf5545df8a033e862e2c406
BLAKE2b-256 checksum
How to use checksums
ac8162da365555a79978ec3786bbce0322b84ac18891e7fc49ac23239fb24b2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page