prism-blanklines


Nameprism-blanklines JSON
Version 0.4.2 PyPI version JSON
download
home_pageNone
SummaryBlank line enforcement for Python code based on CLAUDE.md rules
upload_time2025-10-17 01:58:19
maintainerNone
docs_urlNone
authorGreg Smethells
requires_python>=3.11
licenseGPL-3.0-or-later
keywords blank-lines code-quality formatter python
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # 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

```bash
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

```bash
# 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:

```toml
[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
   ```python
   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`
   ```python
   print('hello')
   someFunction()
   return result
   ```

3. **`import`** - Import statements
   ```python
   import os
   from pathlib import Path
   ```

4. **`control`** - Control structures (`if`/`elif`/`else`, `for`/`else`, `while`/`else`, `try`/`except`/`finally`, `with`)
   ```python
   if condition:
       x = 1
       y = 0

   for item in items:
       prologue(item)
       process(item)
       epilogue(item)

   ```

5. **`definition`** - Function and class definitions
   ```python
   def myFunction():
       pass

   class MyClass:
       pass
   ```

6. **`declaration`** - `global` and `nonlocal` statements
   ```python
   global myVar
   nonlocal count
   ```

7. **`comment`** - Comment lines
   ```python
   # This is a comment
   ```

### Configuration Examples

#### Minimal spacing (compact style)
```toml
[blank_lines]
default_between_different = 0
consecutive_control = 1
consecutive_definition = 1
```

#### Extra spacing (airy style)
```toml
[blank_lines]
default_between_different = 2
consecutive_control = 2
consecutive_definition = 2
```

#### Custom transitions
```toml
[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

```bash
# 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:

```python
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:

```python
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:

```python
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
   ```python
   # 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)
   ```python
   x = 1

   # This comment gets a blank line before it
   y = 2
   ```

3. **After comments** - Existing spacing preserved (leave-as-is policy)
   ```python
   # Comment

   import os  # Existing blank line preserved

   # Comment
   x = 1  # No blank line (preserved)
   ```

## Scope and Blank Lines

Prism applies rules independent of scope:

```python
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`:

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

### CI/CD

```bash
# 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:**
```python
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):**
```python
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.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "prism-blanklines",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "blank-lines, code-quality, formatter, python",
    "author": "Greg Smethells",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/c3/e5/45b2617d8903ac24651f1c1f49e40c8b244642aab8dae82dc9c1f89e7541/prism_blanklines-0.4.2.tar.gz",
    "platform": null,
    "description": "# Prism\n\nA Python code formatter that enforces configurable blank line rules.\n\n## Overview\n\nPrism 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.\n\n## Features\n\n- **Configurable blank line rules** - Customize spacing between different code block types\n- **Smart block detection** - Identifies assignments, function calls, imports, control structures, definitions, and more\n- **Multiline statement support** - Properly handles statements spanning multiple lines\n- **Docstring preservation** - Never modifies content within docstrings\n- **Nested scope handling** - Applies rules independently at each indentation level\n- **Comment-aware processing** - Preserves existing spacing around comment blocks\n- **Atomic file operations** - Safe file writing with automatic rollback on errors\n- **Change detection** - Only modifies files that need formatting\n- **Dry-run mode** - Preview changes without modifying files\n- **Check mode** - Verify formatting without making changes\n\n## Installation\n\n### From Source\n\n```bash\ngit clone https://github.com/yourusername/prism.git\ncd prism\npip install -e .\n```\n\n### Requirements\n\n- Python 3.11 or higher\n- No external dependencies for core functionality\n\n## Quick Start\n\n```bash\n# Format a single file\nprism myfile.py\n\n# Format all Python files in a directory\nprism src/\n\n# Check if files need formatting (exit 1 if changes needed)\nprism --check myfile.py\n\n# Preview changes without applying them\nprism --dry-run myfile.py\n\n# Show detailed processing information\nprism --verbose myfile.py\n\n# Show version\nprism --version\n```\n\n## Configuration\n\n### Default Behavior\n\nBy default, prism uses these rules (aligned with PEP 8):\n- **1 blank line** between different block types\n- **1 blank line** between consecutive control structures (`if`, `for`, `while`, `try`, etc.)\n- **2 blank lines** between consecutive top-level (module level) function/class definitions\n- **1 blank line** between consecutive method definitions inside classes\n- **0 blank lines** between statements of the same type\n  - Exception: **1 blank line** between consecutive control blocks at the same scope \n\n### Configuration File\n\nCreate a `prism.toml` file in your project root to customize blank line rules:\n\n```toml\n[blank_lines]\n# Default spacing between different block types (0-3 blank lines)\ndefault_between_different = 1\n\n# Spacing between consecutive control blocks (if, for, while, try, with)\nconsecutive_control = 1\n\n# Spacing between consecutive definitions (def, class)\nconsecutive_definition = 1\n\n# Indent width for indentation detection (default: 2 spaces)\nindent_width = 2\n\n# Fine-grained transition overrides\n# Format: <from_block>_to_<to_block> = <count>\nassignment_to_call = 2\ncall_to_assignment = 2\nimport_to_assignment = 0\ncontrol_to_definition = 2\n```\n\n### Block Types\n\nPrism recognizes these code block types (in precedence order):\n\n1. **`assignment`** - Variable assignments, list/dict comprehensions, lambda expressions\n   ```python\n   x = 42\n   items = [i for i in range(10)]\n   func = lambda x: x * 2\n   ```\n\n2. **`call`** - Function/method calls, `del`, `assert`, `pass`, `raise`, `yield`, `return`\n   ```python\n   print('hello')\n   someFunction()\n   return result\n   ```\n\n3. **`import`** - Import statements\n   ```python\n   import os\n   from pathlib import Path\n   ```\n\n4. **`control`** - Control structures (`if`/`elif`/`else`, `for`/`else`, `while`/`else`, `try`/`except`/`finally`, `with`)\n   ```python\n   if condition:\n       x = 1\n       y = 0\n\n   for item in items:\n       prologue(item)\n       process(item)\n       epilogue(item)\n\n   ```\n\n5. **`definition`** - Function and class definitions\n   ```python\n   def myFunction():\n       pass\n\n   class MyClass:\n       pass\n   ```\n\n6. **`declaration`** - `global` and `nonlocal` statements\n   ```python\n   global myVar\n   nonlocal count\n   ```\n\n7. **`comment`** - Comment lines\n   ```python\n   # This is a comment\n   ```\n\n### Configuration Examples\n\n#### Minimal spacing (compact style)\n```toml\n[blank_lines]\ndefault_between_different = 0\nconsecutive_control = 1\nconsecutive_definition = 1\n```\n\n#### Extra spacing (airy style)\n```toml\n[blank_lines]\ndefault_between_different = 2\nconsecutive_control = 2\nconsecutive_definition = 2\n```\n\n#### Custom transitions\n```toml\n[blank_lines]\n# Default: 1 blank line between different types\ndefault_between_different = 1\n\n# But no blank lines between imports and assignments\nimport_to_assignment = 0\n\n# And 2 blank lines between import blocks and definitions such as a `class`\nimport_to_definition = 2\n```\n\n### Using Custom Configuration\n\n```bash\n# Use a specific config file\nprism --config custom.toml myfile.py\n\n# Use default configuration (ignore prism.toml if it exists)\nprism --no-config myfile.py\n```\n\n## Block Classification Rules\n\n### Precedence\n\nWhen a statement could match multiple block types, prism uses precedence:\n\n```python\nx = someFunction()  # Assignment (precedence over Call)\nresult = [i for i in range(10)]  # Assignment (comprehension)\n```\n\n### Multiline Statements\n\nMultiline statements are classified as a single unit:\n\n```python\nresult = complexFunction(\n    arg1,\n    arg2,\n    arg3\n)  # Entire statement is classified as Assignment\n```\n\n### Docstrings\n\nDocstring content is never modified - all internal formatting, blank lines, and special characters are preserved exactly:\n\n```python\ndef example():\n    \"\"\"\n    This docstring content is preserved exactly.\n\n    # This is NOT treated as a comment\n\n    All blank lines inside are preserved.\n    \"\"\"\n    pass\n```\n\n## Comment Handling\n\nPrism has special rules for comments:\n\n1. **Consecutive comments** - No blank lines inserted between comment lines\n   ```python\n   # Copyright header line 1\n   # Copyright header line 2\n   # Copyright header line 3\n   ```\n\n2. **Comment breaks** - Blank line added before a comment (unless previous line was also a comment)\n   ```python\n   x = 1\n\n   # This comment gets a blank line before it\n   y = 2\n   ```\n\n3. **After comments** - Existing spacing preserved (leave-as-is policy)\n   ```python\n   # Comment\n\n   import os  # Existing blank line preserved\n\n   # Comment\n   x = 1  # No blank line (preserved)\n   ```\n\n## Scope and Blank Lines\n\nPrism applies rules independent of scope:\n\n```python\ndef outer():\n  x = 1\n  y = 0\n  z = 0\n\n  print('Level 1')\n\n  def inner():\n    y += 1\n\n    print('Level 2')\n\n    if condition:\n      z += 1\n```\n\nRules are applied separately for:\n- Module level (indent 0)\n- Inside `outer()` function (indent 2)\n- Inside `inner()` function (indent 4)\n- Inside `if` block (indent 6)\n\n## Exit Codes\n\n- **0** - Success: No changes needed or changes applied successfully\n- **1** - Failure: Changes needed (in `--check` mode) or processing error occurred\n\n## Integration\n\n### Pre-commit Hook\n\nAdd to `.pre-commit-config.yaml`:\n\n```yaml\nrepos:\n  - repo: local\n    hooks:\n      - id: prism\n        name: prism\n        entry: prism\n        language: system\n        types: [python]\n```\n\n### CI/CD\n\n```bash\n# Check formatting in CI\nprism --check src/\nif [ $? -ne 0 ]; then\n    echo \"Code needs formatting. Run: prism src/\"\n    exit 1\nfi\n```\n\n### Editor Integration\n\nMost editors can be configured to run prism on save or as a format command.\n\n## Examples\n\n### Before and After\n\n**Before:**\n```python\nimport os\nimport sys\ndef main():\n    x = 1\n    y = 2\n    if x > 0:\n        print(x)\n    else:\n        print(y)\n    for i in range(10):\n        process(i)\n    class Helper:\n        pass\n```\n\n**After (with default config):**\n```python\nimport os\nimport sys\n\nx = 1\ny = 2\n\nif x > 0:\n    print(x)\nelse:\n    print(y)\n\nfor i in range(10):\n    process(i)\n\nclass Helper:\n    pass\n```\n\n## Comparison with Other Tools\n\n| Feature                 | Prism           | Black                 | Ruff                  |\n|-------------------------|-----------------|-----------------------|-----------------------|\n| Blank line rules        | \u2705 Configurable | \u2705 Fixed              | \u2705 Fixed              |\n| Scope-aware spacing     | \u2705 Yes          | \u26a0\ufe0f  Limited            | \u26a0\ufe0f  Limited            |\n| Indentation handling    | \u2705 Configurable | \u26a0\ufe0f  Enforces/reformats | \u26a0\ufe0f  Enforces/reformats |\n\n**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.\n\n**Key Differentiators**:\n- **Configurable blank line rules** - Control spacing between any block type transition\n- **Independent scope-level processing** - Rules applied within each scope equally\n- **Works with your indentation** - Detects existing style, never reformats it\n\n**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.\n\n## Troubleshooting\n\n### Files Not Being Modified\n\n1. Check if files already match the rules: `prism --check file.py`\n2. Use verbose mode to see what's happening: `prism --verbose file.py`\n3. Verify your configuration: check `prism.toml` syntax\n\n### Unexpected Blank Lines\n\n1. Review your configuration file (`prism.toml`)\n2. Use `--dry-run` to preview changes: `prism --dry-run file.py`\n3. Check for comment blocks that may trigger special rules\n4. Verify indentation consistency (tabs vs spaces)\n\n### Configuration Not Being Applied\n\n1. Ensure `prism.toml` is in the current directory or specify with `--config`\n2. Check TOML syntax is valid\n3. Verify values are in valid range (0-3)\n4. Check block type names match documentation\n\n## Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for any new functionality\n4. Ensure all tests pass: `pytest`\n5. Run code quality checks: `ruff check` and `ruff format`\n6. Submit a pull request\n\n## License\n\nSee the LICENSE file for details.\n\n## Acknowledgments\n\nPrism was inspired by the philosophy of tools like Black and Ruff - that automated formatting allows developers to focus on logic rather than style.\n",
    "bugtrack_url": null,
    "license": "GPL-3.0-or-later",
    "summary": "Blank line enforcement for Python code based on CLAUDE.md rules",
    "version": "0.4.2",
    "project_urls": {
        "Issues": "https://github.com/gsmethells/prism/issues",
        "Repository": "https://github.com/gsmethells/prism"
    },
    "split_keywords": [
        "blank-lines",
        " code-quality",
        " formatter",
        " python"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "93998440909277029e3a1bd87fdf94f4c816038468939b41919483cce4754ea4",
                "md5": "6facb448f2771b2ca8e3b112dd1b0dff",
                "sha256": "ef3c45295cdbd5010eb80e0fd1909f7d413089d62653a6978a089b8587cb9006"
            },
            "downloads": -1,
            "filename": "prism_blanklines-0.4.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6facb448f2771b2ca8e3b112dd1b0dff",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 34129,
            "upload_time": "2025-10-17T01:58:18",
            "upload_time_iso_8601": "2025-10-17T01:58:18.846500Z",
            "url": "https://files.pythonhosted.org/packages/93/99/8440909277029e3a1bd87fdf94f4c816038468939b41919483cce4754ea4/prism_blanklines-0.4.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c3e545b2617d8903ac24651f1c1f49e40c8b244642aab8dae82dc9c1f89e7541",
                "md5": "034a7b2b7df6bee52ffe21007ba1b832",
                "sha256": "b9ef506672816e291362ea6182b48d1f67f09ba1f9e18dd2dac190c9b9af9832"
            },
            "downloads": -1,
            "filename": "prism_blanklines-0.4.2.tar.gz",
            "has_sig": false,
            "md5_digest": "034a7b2b7df6bee52ffe21007ba1b832",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 58555,
            "upload_time": "2025-10-17T01:58:19",
            "upload_time_iso_8601": "2025-10-17T01:58:19.724523Z",
            "url": "https://files.pythonhosted.org/packages/c3/e5/45b2617d8903ac24651f1c1f49e40c8b244642aab8dae82dc9c1f89e7541/prism_blanklines-0.4.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-17 01:58:19",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "gsmethells",
    "github_project": "prism",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": false,
    "test_requirements": [
        {
            "name": "coverage",
            "specs": [
                [
                    ">=",
                    "5.3"
                ]
            ]
        },
        {
            "name": "pre-commit",
            "specs": []
        },
        {
            "name": "pytest",
            "specs": [
                [
                    ">=",
                    "6.1.1"
                ]
            ]
        },
        {
            "name": "pytest-xdist",
            "specs": [
                [
                    "<",
                    "3.0.2"
                ],
                [
                    ">=",
                    "2.2.1"
                ]
            ]
        },
        {
            "name": "pytest-cov",
            "specs": [
                [
                    ">=",
                    "2.11.1"
                ]
            ]
        },
        {
            "name": "tox",
            "specs": []
        }
    ],
    "lcname": "prism-blanklines"
}
        
Elapsed time: 1.62985s