The uncompromising code formatter.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Exception classes for error handling, constants for default configuration, type definitions, and utility functions for encoding detection and reporting functionality.
Exception hierarchy for handling various error conditions during code formatting.
class NothingChanged(UserWarning):
"""
Raised when reformatted code is identical to source.
Used to indicate that formatting was successful but no changes
were needed. Commonly caught and handled gracefully in tooling.
"""
pass
class InvalidInput(ValueError):
"""
Raised when source code fails all parse attempts.
Indicates that the input code contains syntax errors or uses
unsupported Python syntax that cannot be parsed by Black.
"""
pass
class ASTSafetyError(Exception):
"""
Raised when Black's generated code is not equivalent to source AST.
This indicates a bug in Black's formatting logic where the
output is not semantically equivalent to the input.
"""
passClasses for tracking and reporting formatting results and statistics.
class Changed(Enum):
"""File change status enumeration."""
NO = 0 # File was not changed
CACHED = 1 # File was not changed (result from cache)
YES = 2 # File was changed
@dataclass
class Report:
"""
Tracks formatting results and statistics.
Used by CLI and batch processing to accumulate results
across multiple files and provide summary statistics.
"""
check: bool = False
diff: bool = False
quiet: bool = False
verbose: bool = False
change_count: int = 0
same_count: int = 0
failure_count: int = 0
def done(self, src: Path, changed: Changed) -> None:
"""
Record completion of file processing.
Parameters:
- src: Path of processed file
- changed: Whether file was modified
"""
def failed(self, src: Path, message: str) -> None:
"""
Record processing failure.
Parameters:
- src: Path of failed file
- message: Error message
"""
def path_ignored(self, path: Path, message: str) -> None:
"""
Record ignored path with reason.
Parameters:
- path: Ignored path
- message: Reason for ignoring
"""
@property
def return_code(self) -> int:
"""
Calculate exit code for CLI usage.
Returns:
- 0: Success (no changes needed or changes made successfully)
- 1: Changes needed (when check=True) or formatting errors
"""Classes for managing formatting result caching to improve performance.
@dataclass
class Cache:
"""
File formatting cache management.
Caches formatting results based on file content hash and
configuration to avoid re-formatting unchanged files.
"""
mode: Mode
cache_file: Path
file_data: dict[str, FileData] = field(default_factory=dict)
@classmethod
def read(cls, mode: Mode) -> 'Cache':
"""
Read existing cache from disk.
Parameters:
- mode: Mode configuration for cache key generation
Returns:
Cache instance loaded from disk or new empty cache
"""
@staticmethod
def hash_digest(path: Path) -> str:
"""
Generate hash digest for file content.
Parameters:
- path: Path to file
Returns:
SHA-256 hash of file content and metadata
"""
@staticmethod
def get_file_data(path: Path) -> 'FileData':
"""
Get file metadata for caching.
Parameters:
- path: Path to file
Returns:
FileData with hash and modification time
"""
@dataclass
class FileData:
"""File metadata for cache entries."""
hash: str
mtime: floatFunctions for formatted output with styling support.
def out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None:
"""
Standard output with optional styling.
Parameters:
- message: Message to output (None for newline only)
- nl: Add newline after message
- **styles: Click styling options (fg, bg, bold, etc.)
"""
def err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None:
"""
Error output with optional styling.
Parameters:
- message: Error message to output
- nl: Add newline after message
- **styles: Click styling options
"""
def diff(a: str, b: str, a_name: str, b_name: str) -> str:
"""
Generate unified diff between strings.
Parameters:
- a: Original content
- b: Modified content
- a_name: Name/label for original
- b_name: Name/label for modified
Returns:
Unified diff string
"""
def color_diff(contents: str) -> str:
"""
Add ANSI color codes to diff output.
Parameters:
- contents: Plain diff text
Returns:
Colorized diff with ANSI escape sequences
"""
def ipynb_diff(a: str, b: str, a_name: str, b_name: str) -> str:
"""
Generate diff for Jupyter notebook cells.
Parameters:
- a: Original notebook cell content
- b: Modified notebook cell content
- a_name: Label for original
- b_name: Label for modified
Returns:
Diff formatted for notebook context
"""
def dump_to_file(*output: str, ensure_final_newline: bool = True) -> str:
"""
Write output to temporary file and return path.
Parameters:
- *output: Content strings to write
- ensure_final_newline: Add final newline if missing
Returns:
Path to created temporary file
"""Core parsing functions and AST processing utilities.
def lib2to3_parse(src_txt: str, target_versions: Collection[TargetVersion] = ()) -> Node:
"""
Parse Python source using lib2to3 with version targeting.
Parameters:
- src_txt: Python source code to parse
- target_versions: Python versions to attempt parsing with
Returns:
lib2to3 Node representing parsed AST
Raises:
- InvalidInput: If parsing fails for all target versions
"""
def parse_ast(src: str) -> ast.AST:
"""
Parse source to standard Python AST.
Parameters:
- src: Python source code
Returns:
Python ast.AST object
Raises:
- SyntaxError: If source contains syntax errors
"""
def stringify_ast(node: ast.AST) -> Iterator[str]:
"""
Convert AST to comparable string representation.
Parameters:
- node: AST node to stringify
Yields:
String tokens representing AST structure
"""# Line length default
DEFAULT_LINE_LENGTH: int = 88
# File pattern constants
DEFAULT_EXCLUDES: str = r"/(\.direnv|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|venv|\.svn|\.ipynb_checkpoints|_build|buck-out|build|dist)/"
DEFAULT_INCLUDES: str = r"(\.pyi?|\.ipynb)$"
# Stdin placeholder for file processing
STDIN_PLACEHOLDER: str = "__BLACK_STDIN_FILENAME__"# Content types
FileContent = str
Encoding = str
NewLine = str
# Legacy compatibility
FileMode = Mode
# AST types (from blib2to3)
Node = blib2to3.pytree.Node
Leaf = blib2to3.pytree.Leaf
LN = Union[Leaf, Node] # Leaf or Node
NL = blib2to3.pytree.Base # Node or Leaf base
# Path types
PathLike = Union[str, Path, os.PathLike[str]]
# Collection types
Collection = typing.Collection
Sequence = typing.Sequence
Iterator = typing.Iteratorimport black
from pathlib import Path
def safe_format_file(file_path: Path) -> tuple[bool, str]:
"""Safely format file with comprehensive error handling."""
try:
changed = black.format_file_in_place(
file_path,
fast=False,
mode=black.Mode(),
write_back=black.WriteBack.YES
)
return changed, "Success"
except black.NothingChanged:
return False, "No changes needed"
except black.InvalidInput as e:
return False, f"Syntax error: {e}"
except black.ASTSafetyError as e:
return False, f"AST safety check failed: {e}"
except PermissionError:
return False, "Permission denied"
except Exception as e:
return False, f"Unexpected error: {e}"
# Usage
changed, message = safe_format_file(Path("example.py"))
print(f"Result: {message}")from pathlib import Path
# Create report for batch processing
report = black.Report(check=False, diff=True, verbose=True)
files = [Path("file1.py"), Path("file2.py"), Path("file3.py")]
for file_path in files:
try:
changed = black.format_file_in_place(
file_path,
fast=False,
mode=black.Mode(),
write_back=black.WriteBack.YES
)
# Record result
status = black.Changed.YES if changed else black.Changed.NO
report.done(file_path, status)
except Exception as e:
report.failed(file_path, str(e))
# Print summary
print(f"Files changed: {report.change_count}")
print(f"Files unchanged: {report.same_count}")
print(f"Files failed: {report.failure_count}")
print(f"Exit code: {report.return_code}")# Read existing cache
mode = black.Mode(line_length=88)
cache = black.Cache.read(mode)
# Check if file needs formatting
file_path = Path("example.py")
file_data = black.Cache.get_file_data(file_path)
file_hash = black.Cache.hash_digest(file_path)
if file_hash in cache.file_data:
print("File already processed (cached)")
else:
print("File needs processing")
# ... format file ...
# Update cache with new file data
cache.file_data[file_hash] = file_dataimport black
# Styled output
black.out("Formatting completed successfully", fg="green", bold=True)
black.err("Formatting failed", fg="red", bold=True)
# Generate and display diff
original = "def hello(name):print(f'Hello {name}!')"
formatted = black.format_str(original, mode=black.Mode())
diff_text = black.diff(original, formatted, "original", "formatted")
colored_diff = black.color_diff(diff_text)
print("Plain diff:")
print(diff_text)
print("\nColored diff:")
print(colored_diff)
# Write to temporary file
temp_path = black.dump_to_file(formatted, ensure_final_newline=True)
print(f"Wrote formatted code to: {temp_path}")import ast
import black
code = """
def example_function(x: int, y: str = "default") -> str:
return f"{x}: {y}"
"""
# Parse with lib2to3 (Black's parser)
lib2to3_tree = black.lib2to3_parse(code)
print(f"lib2to3 tree type: {type(lib2to3_tree)}")
# Parse with standard AST
ast_tree = black.parse_ast(code)
print(f"AST tree type: {type(ast_tree)}")
# Convert AST to comparable format
ast_strings = list(black.stringify_ast(ast_tree))
print(f"AST representation: {ast_strings[:5]}...") # First 5 tokens
# Use in equivalence checking
formatted = black.format_str(code, mode=black.Mode())
try:
black.assert_equivalent(code, formatted)
print("Formatted code is AST-equivalent to original")
except AssertionError as e:
print(f"AST equivalence check failed: {e}")from typing import Union, Optional
import black
def format_code_safe(
code: str,
*,
mode: Optional[black.Mode] = None,
lines: Optional[black.Collection[tuple[int, int]]] = None
) -> Union[str, None]:
"""
Type-safe code formatting function.
Returns None if formatting fails, formatted string if successful.
"""
if mode is None:
mode = black.Mode()
if lines is None:
lines = ()
try:
return black.format_str(code, mode=mode, lines=lines)
except (black.InvalidInput, black.NothingChanged):
return None
except Exception:
return None
# Usage with type checking
result = format_code_safe("def hello(): pass")
if result is not None:
print("Formatted successfully")
print(result)
else:
print("Formatting failed")Install with Tessl CLI
npx tessl i tessl/pypi-black