Skip to main content

Blank Line Enforcement for Python

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

blanken-0.1.2.tar.gz (26.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

blanken-0.1.2-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file blanken-0.1.2.tar.gz.

File metadata

  • Download URL: blanken-0.1.2.tar.gz
  • Upload date:
  • Size: 26.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for blanken-0.1.2.tar.gz
Algorithm Hash digest
SHA256 7f6c603225278f342aea21766535178f599309e3d70f8a160c4350a3de21b70a
MD5 c019384602ee0a98e20242e261840a55
BLAKE2b-256 ca21a954fb85a0fe896495442f903c4967e27691e7d8b51dc0f7f86e2c145929

See more details on using hashes here.

File details

Details for the file blanken-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: blanken-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for blanken-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5018bc43c6f6d2bc8ef1118ddb4fdd4a43bb51837bf5545df8a033e862e2c406
MD5 90cf3bfbfc042ee8fbbe07698a46b527
BLAKE2b-256 ac8162da365555a79978ec3786bbce0322b84ac18891e7fc49ac23239fb24b2c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page