Skip to main content
Archived

This project has been archived by its maintainers, and is no longer receiving any updates.

Prism

A Python code formatter that enforces configurable blank line rules.

Overview

Prism is a code formatting tool that intelligently manages blank lines in Python code, similar to how black handles code formatting. It applies sophisticated rules to ensure consistent spacing between different types of code blocks while preserving your code's logical structure and documentation.

Features

  • Configurable blank line rules - Customize spacing between different code block types
  • Smart block detection - Identifies assignments, function calls, imports, control structures, definitions, and more
  • Multiline statement support - Properly handles statements spanning multiple lines
  • Docstring preservation - Never modifies content within docstrings
  • Nested scope handling - Applies rules independently at each indentation level
  • Comment-aware processing - Preserves existing spacing around comment blocks
  • Atomic file operations - Safe file writing with automatic rollback on errors
  • Change detection - Only modifies files that need formatting
  • Dry-run mode - Preview changes without modifying files
  • Check mode - Verify formatting without making changes

Installation

From Source

git clone https://github.com/yourusername/prism.git
cd prism
pip install -e .

Requirements

  • Python 3.11 or higher
  • No external dependencies for core functionality

Quick Start

# Format a single file
prism myfile.py

# Format all Python files in a directory
prism src/

# Check if files need formatting (exit 1 if changes needed)
prism --check myfile.py

# Preview changes without applying them
prism --dry-run myfile.py

# Show detailed processing information
prism --verbose myfile.py

# Show version
prism --version

Configuration

Default Behavior

By default, prism uses these rules (aligned with PEP 8):

  • 1 blank line between different block types
  • 1 blank line between consecutive control structures (if, for, while, try, etc.)
  • 2 blank lines between consecutive top-level (module level) function/class definitions
  • 1 blank line between consecutive method definitions inside classes
  • 0 blank lines between statements of the same type
    • Exception: 1 blank line between consecutive control blocks at the same scope

Configuration File

Create a prism.toml file in your project root to customize blank line rules:

[blank_lines]
# Default spacing between different block types (0-3 blank lines)
default_between_different = 1

# Spacing between consecutive control blocks (if, for, while, try, with)
consecutive_control = 1

# Spacing between consecutive definitions (def, class)
consecutive_definition = 1

# Indent width for indentation detection (default: 2 spaces)
indent_width = 2

# Fine-grained transition overrides
# Format: <from_block>_to_<to_block> = <count>
assignment_to_call = 2
call_to_assignment = 2
import_to_assignment = 0
control_to_definition = 2

Block Types

Prism recognizes these code block types (in precedence order):

  1. assignment - Variable assignments, list/dict comprehensions, lambda expressions

    x = 42
    items = [i for i in range(10)]
    func = lambda x: x * 2
    
  2. call - Function/method calls, del, assert, pass, raise, yield, return

    print('hello')
    someFunction()
    return result
    
  3. import - Import statements

    import os
    from pathlib import Path
    
  4. control - Control structures (if/elif/else, for/else, while/else, try/except/finally, with)

    if condition:
        x = 1
        y = 0
    
    for item in items:
        prologue(item)
        process(item)
        epilogue(item)
    
  5. definition - Function and class definitions

    def myFunction():
        pass
    
    class MyClass:
        pass
    
  6. declaration - global and nonlocal statements

    global myVar
    nonlocal count
    
  7. comment - Comment lines

    # This is a comment
    

Configuration Examples

Minimal spacing (compact style)

[blank_lines]
default_between_different = 0
consecutive_control = 1
consecutive_definition = 1

Extra spacing (airy style)

[blank_lines]
default_between_different = 2
consecutive_control = 2
consecutive_definition = 2

Custom transitions

[blank_lines]
# Default: 1 blank line between different types
default_between_different = 1

# But no blank lines between imports and assignments
import_to_assignment = 0

# And 2 blank lines between import blocks and definitions such as a `class`
import_to_definition = 2

Using Custom Configuration

# Use a specific config file
prism --config custom.toml myfile.py

# Use default configuration (ignore prism.toml if it exists)
prism --no-config myfile.py

Block Classification Rules

Precedence

When a statement could match multiple block types, prism uses precedence:

x = someFunction()  # Assignment (precedence over Call)
result = [i for i in range(10)]  # Assignment (comprehension)

Multiline Statements

Multiline statements are classified as a single unit:

result = complexFunction(
    arg1,
    arg2,
    arg3
)  # Entire statement is classified as Assignment

Docstrings

Docstring content is never modified - all internal formatting, blank lines, and special characters are preserved exactly:

def example():
    """
    This docstring content is preserved exactly.

    # This is NOT treated as a comment

    All blank lines inside are preserved.
    """
    pass

Comment Handling

Prism has special rules for comments:

  1. Consecutive comments - No blank lines inserted between comment lines

    # Copyright header line 1
    # Copyright header line 2
    # Copyright header line 3
    
  2. Comment breaks - Blank line added before a comment (unless previous line was also a comment)

    x = 1
    
    # This comment gets a blank line before it
    y = 2
    
  3. After comments - Existing spacing preserved (leave-as-is policy)

    # Comment
    
    import os  # Existing blank line preserved
    
    # Comment
    x = 1  # No blank line (preserved)
    

Scope and Blank Lines

Prism applies rules independent of scope:

def outer():
  x = 1
  y = 0
  z = 0

  print('Level 1')

  def inner():
    y += 1

    print('Level 2')

    if condition:
      z += 1

Rules are applied separately for:

  • Module level (indent 0)
  • Inside outer() function (indent 2)
  • Inside inner() function (indent 4)
  • Inside if block (indent 6)

Exit Codes

  • 0 - Success: No changes needed or changes applied successfully
  • 1 - Failure: Changes needed (in --check mode) or processing error occurred

Integration

Pre-commit Hook

Add to .pre-commit-config.yaml:

repos:
  - repo: local
    hooks:
      - id: prism
        name: prism
        entry: prism
        language: system
        types: [python]

CI/CD

# Check formatting in CI
prism --check src/
if [ $? -ne 0 ]; then
    echo "Code needs formatting. Run: prism src/"
    exit 1
fi

Editor Integration

Most editors can be configured to run prism on save or as a format command.

Examples

Before and After

Before:

import os
import sys
def main():
    x = 1
    y = 2
    if x > 0:
        print(x)
    else:
        print(y)
    for i in range(10):
        process(i)
    class Helper:
        pass

After (with default config):

import os
import sys

x = 1
y = 2

if x > 0:
    print(x)
else:
    print(y)

for i in range(10):
    process(i)

class Helper:
    pass

Comparison with Other Tools

Feature Prism Black Ruff
Blank line rules ✅ Configurable ✅ Fixed ✅ Fixed
Scope-aware spacing ✅ Yes ⚠️ Limited ⚠️ Limited
Indentation handling ✅ Configurable ⚠️ Enforces/reformats ⚠️ Enforces/reformats

Prism's Focus: Prism solves one problem exceptionally well - scope-aware, configurable blank line enforcement. This is a unique capability that Black and Ruff don't provide comprehensively.

Key Differentiators:

  • Configurable blank line rules - Control spacing between any block type transition
  • Independent scope-level processing - Rules applied within each scope equally
  • Works with your indentation - Detects existing style, never reformats it

Philosophy: Prism is designed to work alongside Black or Ruff, not replace them. Use Black/Ruff for general formatting (line length, quotes, imports) and Prism for blank line intelligence.

Troubleshooting

Files Not Being Modified

  1. Check if files already match the rules: prism --check file.py
  2. Use verbose mode to see what's happening: prism --verbose file.py
  3. Verify your configuration: check prism.toml syntax

Unexpected Blank Lines

  1. Review your configuration file (prism.toml)
  2. Use --dry-run to preview changes: prism --dry-run file.py
  3. Check for comment blocks that may trigger special rules
  4. Verify indentation consistency (tabs vs spaces)

Configuration Not Being Applied

  1. Ensure prism.toml is in the current directory or specify with --config
  2. Check TOML syntax is valid
  3. Verify values are in valid range (0-3)
  4. Check block type names match documentation

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for any new functionality
  4. Ensure all tests pass: pytest
  5. Run code quality checks: ruff check and ruff format
  6. Submit a pull request

License

See the LICENSE file for details.

Acknowledgments

Prism was inspired by the philosophy of tools like Black and Ruff - that automated formatting allows developers to focus on logic rather than style.

Metadata

Release files for prism-blanklines 0.5.6

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

Source distribution (sdist)

Source distribution for prism-blanklines 0.5.6
File Size Uploaded
prism_blanklines-0.5.6.tar.gz 69.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for prism-blanklines 0.5.6
File Interpreter ABI Platform
prism_blanklines-0.5.6-py3-none-any.whl Python 3 none any Details

Total release size: 104.7 kB

Release files / prism_blanklines-0.5.6.tar.gz

Download URL prism_blanklines-0.5.6.tar.gz
Size 69.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ba41998b57c03a7eecb90a5f2b9812ec1e56665cab9d114ca428365fbac7ced2
BLAKE2b-256 checksum
How to use checksums
e9c8070526ab69aece4200e572570d12194f31463f659a39597dc87c32251a08
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.6

Release files / prism_blanklines-0.5.6-py3-none-any.whl

Download URL prism_blanklines-0.5.6-py3-none-any.whl
Size 34.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1e1562f2aa7cac35c8032a41fb2b9e6c0b10241488c7ee6fcc8c448fa8a1db0a
BLAKE2b-256 checksum
How to use checksums
1530035e84c89b498031b9471bb6b00a36f25d8ae4c0ff3e2df91f8845bd010a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.6
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