Black

Code QualityFormatterPythonDevOpsAuto-formattingPEP8Uncompromising

DevOps Tool

Black

Overview

Black is Python's "uncompromising" code formatter that automatically applies PEP 8 compliant code style, eliminating formatting discussions. Adopting an "opinionated" approach that intentionally minimizes configuration options and forcibly applies consistent code style. The 2025 version, Black 25.1.0, introduces a new stable style with enhanced formatting features including Unicode character normalization, improved type annotations, and enhanced docstring detection.

Details

Black is a Python-specific code formatter managed by the Python Software Foundation (PSF), established since its 2018 release as the tool to "end all code formatting debates" in Python development. Black 25.1.0 in 2025 introduces a new stable style (2025 stable style) providing more sophisticated formatting capabilities.

Key Features

  • Uncompromising Formatting: Intentionally minimizes configuration options, emphasizing consistency
  • Full PEP 8 Compliance: Strictly follows Python's official style guide
  • High-Speed Execution: Achieves fast processing through partial Rust-based implementation
  • Jupyter Notebook Support: Automatic code cell formatting in Jupyter environments
  • Editor Integration: Support for major editors like VS Code, PyCharm, Vim, etc.
  • CI/CD Integration: Automated checking and fixing in GitHub Actions, GitLab CI, etc.
  • Deterministic Output: Same results with multiple executions, complete idempotency
  • Minimal Configuration: Limited configuration options via pyproject.toml

2025 New Features

  • Unicode Character Normalization: Lowercase normalization of Unicode escape characters in strings
  • Improved Docstring Detection: Enhanced consistency in docstring recognition
  • Enhanced Type Annotation Formatting: Automatic addition of trailing commas to function parameters
  • Case Statement Optimization: Removal of redundant parentheses in if guards and line length control
  • Improved Comment Processing: Stop normalizing whitespace before # fmt: skip comments

Pros and Cons

Pros

  • Immediate style unification across Python development teams without configuration
  • Complete elimination of formatting discussions and code review time
  • Generation of standard, readable Python code through PEP 8 compliance
  • Practical execution time for large projects through high-speed processing
  • Stable formatting results through deterministic output
  • Support for scientific computing and data science environments through Jupyter Notebook integration
  • Real-time auto-formatting experience through editor integration
  • Reduced learning costs and maintenance burden through minimal configuration
  • Automated quality assurance and code quality gates through CI/CD integration
  • Language-optimized formatting through Python-specific specialization

Cons

  • Extreme limitation of customization (intentional design but lacks flexibility)
  • Massive changes when applying to existing code, causing increased diffs
  • Forced divergence from existing personal/team coding habits
  • Difficulty supporting specific formatting requirements (corporate standards, etc.)
  • Limited adjustment of line length restrictions (default 88 characters)
  • Risk of massive changes when bulk-applying to legacy codebases
  • Complexity of integration settings in some editors
  • Inability to use unified tools in multilingual projects due to Python-only focus
  • Difficulty in fine-grained control over formatting results
  • Restriction of developer expression freedom through excessive opinion enforcement

Reference Links

Code Examples

Installation and Basic Setup

# Basic installation
pip install black

# Installation with Jupyter Notebook support
pip install "black[jupyter]"

# Specific version installation
pip install black==25.1.0

# Add as development dependency
pip install --save-dev black

# Poetry dependency management
poetry add --group dev black

# Add to requirements.txt
echo "black>=25.1.0" >> requirements-dev.txt

# Verify installation
black --version
black --help

Basic Execution Methods

# Format single file
black script.py

# Format entire directory
black src/

# Multiple pattern specification
black src/ tests/ scripts/

# Check mode (verify without changes)
black --check src/

# Show diff (verify changes)
black --diff src/

# Verbose output
black --verbose src/

# Quiet mode (output only on errors)
black --quiet src/

# Exclude specific files
black src/ --exclude="migrations|__pycache__|\.venv"

# Specify line length
black --line-length 100 src/

# Jupyter Notebook support
black notebook.ipynb

Configuration File (pyproject.toml)

# pyproject.toml - Black configuration
[tool.black]
line-length = 88                    # Line length setting (default 88)
target-version = ['py39', 'py310', 'py311', 'py312']  # Target Python versions
include = '\.pyi?$'                 # Target file patterns
extend-exclude = '''
# Exclusion patterns (regex)
/(
  # Directory exclusions
    \.eggs
  | \.git
  | \.hg
  | \.mypy_cache
  | \.tox
  | \.venv
  | _build
  | buck-out
  | build
  | dist
  | migrations
  
  # File exclusions
  | foo.py
)/
'''

# Enable preview features
preview = true

# Disable string normalization (maintain single quotes)
skip-string-normalization = false

# Disable magic trailing comma
skip-magic-trailing-comma = false

# Experimental features
experimental-string-processing = false

# Jupyter notebook configuration
jupyter = true

Editor Integration Settings

VS Code Configuration

// .vscode/settings.json
{
  "python.formatting.provider": "black",
  "python.formatting.blackArgs": [
    "--line-length=88",
    "--target-version=py312"
  ],
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.organizeImports": true
  },
  "[python]": {
    "editor.defaultFormatter": "ms-python.black-formatter",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports": true
    }
  }
}

// Black extension configuration
{
  "black-formatter.args": [
    "--line-length=88",
    "--preview"
  ],
  "black-formatter.importStrategy": "fromEnvironment"
}

PyCharm Configuration

# PyCharm External Tool configuration
# File → Settings → Tools → External Tools → Add

Name: Black
Description: Black Python formatter
Program: black
Arguments: $FilePath$
Working Directory: $ProjectFileDir$

# Or configure as File Watcher
# File Type: Python
# Scope: Project Files
# Program: black
# Arguments: $FilePath$

pre-commit Integration

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black-pre-commit-mirror
    rev: 25.1.0
    hooks:
      - id: black
        language_version: python3.12
        args: [--line-length=88, --target-version=py312]
      
      # Jupyter Notebook support
      - id: black-jupyter
        language_version: python3.12
        args: [--line-length=88, --target-version=py312]

  # Combine with other hooks
  - repo: https://github.com/pycqa/isort
    rev: 5.13.2
    hooks:
      - id: isort
        args: ["--profile", "black"]
        
  - repo: https://github.com/pycqa/flake8
    rev: 7.0.0
    hooks:
      - id: flake8
        args: [--extend-ignore=E203,W503,E501]
# pre-commit setup
pip install pre-commit
pre-commit install
pre-commit autoupdate

# Manual execution
pre-commit run black --all-files
pre-commit run --all-files

CI/CD Integration Examples

GitHub Actions

# .github/workflows/black.yml
name: Black Code Formatter

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  black:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.12'
    
    - name: Install Black
      run: pip install black[jupyter]==25.1.0
    
    - name: Check formatting with Black
      run: black --check --diff src/
    
    - name: Format code with Black (if check fails)
      if: failure()
      run: black src/
    
    - name: Commit formatting changes
      if: failure() && github.event_name == 'pull_request'
      run: |
        git config --local user.email "[email protected]"
        git config --local user.name "GitHub Action"
        git add -A
        git commit -m "Apply Black formatting" -a || exit 0
        git push

tox Integration

# tox.ini
[tox]
envlist = py39,py310,py311,py312,black,flake8

[testenv:black]
deps = black[jupyter]==25.1.0
commands = black --check --diff src/ tests/

[testenv:black-format]
deps = black[jupyter]==25.1.0
commands = black src/ tests/

# Makefile integration
format:
	black src/ tests/
	isort src/ tests/

check:
	black --check --diff src/ tests/
	isort --check-only src/ tests/
	flake8 src/ tests/

Integration with Other Tools

isort Integration

# pyproject.toml - isort configuration
[tool.isort]
profile = "black"                    # Black-compatible profile
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true

flake8 Integration

# .flake8 or setup.cfg
[flake8]
max-line-length = 88
extend-ignore = 
    # Ignore rules that conflict with Black
    E203,  # whitespace before ':'
    W503,  # line break before binary operator
    E501   # line too long (handled by Black)

mypy Integration

# pyproject.toml - mypy configuration
[tool.mypy]
python_version = "3.12"
strict = true

# Compatibility with Black-formatted code
show_error_codes = true
pretty = true
color_output = true

Jupyter Notebook Integration

# Jupyter Notebook specific formatting
black notebook.ipynb

# Multiple notebooks
black notebooks/

# Install Jupyter Lab extension
pip install jupyterlab-code-formatter black

# JupyterLab configuration
jupyter lab --generate-config
# Using Black within Jupyter
# Magic command
%load_ext lab_black

# Auto-format cell
%%black
def poorly_formatted_function(x,y,z):
    return x+y+z

# Result: automatically formatted
def poorly_formatted_function(x, y, z):
    return x + y + z

Actual Formatting Examples

Before Formatting

# Example of poor formatting
def calculate_total(items,tax_rate=0.1,discount=0):
    total=0
    for item in items:
        price=item.get('price',0)
        quantity=item.get('quantity',1)
        total+=price*quantity
    
    tax=total*tax_rate
    final_total=total+tax-discount
    return final_total

class ProductManager:
    def __init__(self,products=[]):
        self.products=products
        
    def add_product(self,name,price,category='general'):
        product={'name':name,'price':price,'category':category}
        self.products.append(product)
        
    def get_products_by_category(self,category):
        return [p for p in self.products if p['category']==category]

After Formatting

# Formatted by Black
def calculate_total(items, tax_rate=0.1, discount=0):
    total = 0
    for item in items:
        price = item.get("price", 0)
        quantity = item.get("quantity", 1)
        total += price * quantity

    tax = total * tax_rate
    final_total = total + tax - discount
    return final_total


class ProductManager:
    def __init__(self, products=None):
        self.products = products or []

    def add_product(self, name, price, category="general"):
        product = {"name": name, "price": price, "category": category}
        self.products.append(product)

    def get_products_by_category(self, category):
        return [p for p in self.products if p["category"] == category]

Advanced Configuration and Workflows

# Makefile integration example
.PHONY: format check test

format:
	black src/ tests/
	isort src/ tests/

check:
	black --check --diff src/ tests/
	isort --check-only --diff src/ tests/
	flake8 src/ tests/
	mypy src/

test: check
	pytest tests/ -v --cov=src/

ci: format test
	echo "CI complete"

# Development script
#!/bin/bash
# scripts/format.sh
set -e

echo "Formatting Python code with Black..."
black src/ tests/

echo "Sorting imports with isort..."
isort src/ tests/

echo "Checking with flake8..."
flake8 src/ tests/

echo "Type checking with mypy..."
mypy src/

echo "Running tests..."
pytest tests/

echo "✅ All checks passed!"