The uncompromising code formatter.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Complete CLI implementation providing file discovery, configuration loading, batch processing, and various output modes for formatting Python code from the command line.
The primary command-line interface function with comprehensive option parsing and file processing.
def main(
ctx: click.Context,
code: Optional[str] = None,
line_length: int = DEFAULT_LINE_LENGTH,
target_version: list[TargetVersion] = [],
pyi: bool = False,
ipynb: bool = False,
python_cell_magics: Sequence[str] = [],
skip_source_first_line: bool = False,
skip_string_normalization: bool = False,
skip_magic_trailing_comma: bool = False,
preview: bool = False,
unstable: bool = False,
enable_unstable_feature: list[Preview] = [],
check: bool = False,
diff: bool = False,
color: bool = False,
line_ranges: Sequence[str] = [],
fast: bool = False,
required_version: Optional[str] = None,
exclude: Optional[Pattern[str]] = None,
extend_exclude: Optional[Pattern[str]] = None,
force_exclude: Optional[Pattern[str]] = None,
stdin_filename: Optional[str] = None,
include: Pattern[str] = DEFAULT_INCLUDES,
workers: Optional[int] = None,
quiet: bool = False,
verbose: bool = False,
src: tuple[str, ...] = (),
config: Optional[str] = None
) -> None:
"""
Main CLI entry point with comprehensive option parsing.
Parameters:
- ctx: Click context for CLI handling
- code: Optional code string to format (instead of files)
- line_length: Maximum line length (default 88)
- target_version: List of Python versions to target
- pyi: Format as .pyi stub file
- ipynb: Format as Jupyter notebook
- python_cell_magics: IPython cell magics to recognize as Python
- skip_source_first_line: Skip shebang lines
- skip_string_normalization: Don't normalize string quotes
- skip_magic_trailing_comma: Don't use trailing comma splitting
- preview: Enable preview features
- unstable: Enable unstable features
- enable_unstable_feature: List of specific preview features to enable
- check: Exit with error if reformatting needed
- diff: Output unified diff instead of rewriting
- color: Colorize diff output
- line_ranges: Line ranges to format (e.g., "1-10,20-30")
- fast: Skip AST safety checks
- required_version: Require specific Black version
- exclude: File pattern to exclude (regex)
- extend_exclude: Additional exclude pattern
- force_exclude: Force exclude pattern (overrides include)
- stdin_filename: Filename for stdin input
- include: File pattern to include (default: Python files)
- workers: Number of parallel workers
- quiet: Minimal output
- verbose: Detailed output
- src: Source files/directories to format
- config: Configuration file path
Note:
Decorated with comprehensive Click options for all CLI functionality
"""Functions for discovering and filtering source files to format.
def get_sources(
*,
root: Path,
src: tuple[str, ...],
quiet: bool,
verbose: bool,
include: Pattern[str],
exclude: Optional[Pattern[str]],
extend_exclude: Optional[Pattern[str]],
force_exclude: Optional[Pattern[str]],
report: Report,
stdin_filename: Optional[str]
) -> set[Path]:
"""
Compute the set of files to format.
Parameters:
- root: Project root directory
- src: Source file/directory paths
- quiet: Suppress output messages
- verbose: Enable detailed output
- include: File pattern to include
- exclude: Base exclude pattern
- extend_exclude: Additional exclude pattern
- force_exclude: Force exclude (overrides include)
- report: Report object for tracking results
- stdin_filename: Filename when reading from stdin
Returns:
Set of Path objects to format
Note:
- Automatically discovers .py, .pyi, and .ipynb files
- Respects .gitignore patterns
- Handles recursive directory traversal
"""Functions for loading and parsing Black configuration from pyproject.toml files.
def read_pyproject_toml(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Optional[str]:
"""
Parse Black configuration from pyproject.toml files.
Parameters:
- ctx: Click context
- param: Click parameter being processed
- value: Path to configuration file (optional)
Returns:
Path to found configuration file, or None
Note:
- Searches for pyproject.toml in project hierarchy
- Loads [tool.black] section configuration
- Merges with command-line arguments
"""
def validate_regex(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Optional[Pattern[str]]:
"""
Validate and compile regex patterns for file filtering.
Parameters:
- ctx: Click context
- param: Click parameter being validated
- value: Regex pattern string
Returns:
Compiled regex Pattern object, or None
Raises:
- click.BadParameter: If regex is invalid
"""Utilities from the black.files module for project structure analysis.
def find_project_root(srcs: tuple[str, ...], stdin_filename: Optional[str] = None) -> tuple[Path, str]:
"""
Find the project root directory and reason.
Parameters:
- srcs: Source file/directory paths
- stdin_filename: Filename for stdin processing
Returns:
Tuple of (project_root_path, reason_string)
"""
def find_pyproject_toml(path_search_start: tuple[str, ...], stdin_filename: Optional[str] = None) -> Optional[str]:
"""
Find pyproject.toml configuration file.
Parameters:
- path_search_start: Paths to start search from
- stdin_filename: Filename for stdin processing
Returns:
Path to pyproject.toml file, or None if not found
"""
def find_user_pyproject_toml() -> Path:
"""
Find user-level pyproject.toml configuration.
Returns:
Path to user configuration file
"""
def parse_pyproject_toml(path_config: str) -> dict[str, Any]:
"""
Parse Black configuration from pyproject.toml file.
Parameters:
- path_config: Path to configuration file
Returns:
Dictionary of configuration values
Raises:
- OSError: If file cannot be read
- tomllib.TOMLDecodeError: If TOML is invalid
"""def gen_python_files(
paths: Iterable[Path],
root: Path,
include: Pattern[str],
exclude: Pattern[str],
extend_exclude: Optional[Pattern[str]],
force_exclude: Optional[Pattern[str]],
report: Report,
gitignore: Optional[PathSpec] = None
) -> Iterator[Path]:
"""
Generate Python files from given paths with filtering.
Parameters:
- paths: Iterable of source paths
- root: Project root directory
- include: Include pattern
- exclude: Exclude pattern
- extend_exclude: Additional exclude pattern
- force_exclude: Force exclude pattern
- report: Report object for tracking
- gitignore: Optional gitignore patterns
Yields:
Path objects for files to process
"""
def get_gitignore(root: Path) -> PathSpec:
"""
Get gitignore patterns for the project.
Parameters:
- root: Project root directory
Returns:
PathSpec object with gitignore patterns
"""
def path_is_excluded(
normalized_path: str,
pattern: Optional[Pattern[str]]
) -> bool:
"""
Check if path matches exclusion pattern.
Parameters:
- normalized_path: Normalized file path
- pattern: Regex pattern to match against
Returns:
True if path should be excluded
"""Functions for parsing and handling line range specifications.
def parse_line_ranges(line_ranges: Sequence[str]) -> list[tuple[int, int]]:
"""
Parse line range specifications from CLI arguments.
Parameters:
- line_ranges: List of line range strings (e.g., ["1-10", "20-30"])
Returns:
List of (start_line, end_line) tuples
Note:
- Supports formats: "N", "N-M", "N-", "-M"
- Line numbers are 1-indexed
- Overlapping ranges are merged
Raises:
- ValueError: If range format is invalid
"""
def sanitized_lines(
src_contents: str,
lines: Collection[tuple[int, int]]
) -> Collection[tuple[int, int]]:
"""
Sanitize line ranges against actual file content.
Parameters:
- src_contents: Source file content
- lines: Line ranges to sanitize
Returns:
Sanitized line ranges within file bounds
"""
def adjusted_lines(
original_lines: Collection[tuple[int, int]],
src_contents: str,
dst_contents: str
) -> Collection[tuple[int, int]]:
"""
Adjust line ranges after formatting changes.
Parameters:
- original_lines: Original line ranges
- src_contents: Original file content
- dst_contents: Formatted file content
Returns:
Adjusted line ranges accounting for formatting changes
"""import subprocess
import sys
# Format single file
result = subprocess.run([
sys.executable, "-m", "black", "script.py"
], capture_output=True, text=True)
# Format with options
result = subprocess.run([
sys.executable, "-m", "black",
"--line-length", "79",
"--target-version", "py39",
"--diff",
"src/"
], capture_output=True, text=True)
print(result.stdout)import click
import black
# Create Click context and call main
ctx = click.Context(black.main)
try:
black.main.invoke(ctx,
src=('src/',),
line_length=79,
target_version=[black.TargetVersion.PY39],
diff=True,
check=False
)
except SystemExit as e:
print(f"Exit code: {e.code}")from pathlib import Path
import re
# Setup for file discovery
root = Path(".")
src_paths = ("src/", "tests/")
include = re.compile(r"\.pyi?$")
exclude = re.compile(r"/(\.git|__pycache__|build)/")
report = black.Report()
# Find files to format
sources = black.get_sources(
root=root,
src=src_paths,
quiet=False,
verbose=True,
include=include,
exclude=exclude,
extend_exclude=None,
force_exclude=None,
report=report,
stdin_filename=None
)
print(f"Found {len(sources)} files to format:")
for source in sorted(sources):
print(f" {source}")# Find and load configuration
config_path = black.find_pyproject_toml((".",), stdin_filename=None)
if config_path:
config = black.parse_pyproject_toml(config_path)
print(f"Loaded config from {config_path}:")
print(config)
# Apply config to mode
mode_kwargs = {}
if "line_length" in config:
mode_kwargs["line_length"] = config["line_length"]
if "target_version" in config:
target_versions = {
black.TargetVersion[v.upper().replace(".", "")]
for v in config["target_version"]
}
mode_kwargs["target_versions"] = target_versions
mode = black.Mode(**mode_kwargs)# Parse line ranges from CLI-style strings
range_specs = ["1-10", "20-25", "50-"]
line_ranges = black.parse_line_ranges(range_specs)
print(f"Parsed ranges: {line_ranges}")
# Format specific lines only
code = open("example.py").read()
formatted = black.format_str(
code,
mode=black.Mode(),
lines=line_ranges
)from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
def format_file(file_path: Path) -> bool:
"""Format a single file and return if changed."""
try:
return black.format_file_in_place(
file_path,
fast=False,
mode=black.Mode(),
write_back=black.WriteBack.YES
)
except Exception as e:
print(f"Error formatting {file_path}: {e}")
return False
# Process files in parallel
source_files = list(Path("src").glob("**/*.py"))
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(format_file, source_files))
changed_count = sum(results)
print(f"Formatted {changed_count} files")import io
import contextlib
# Capture CLI output
output_buffer = io.StringIO()
with contextlib.redirect_stdout(output_buffer):
try:
ctx = click.Context(black.main)
black.main.invoke(ctx,
src=('example.py',),
diff=True,
color=False
)
except SystemExit:
pass
diff_output = output_buffer.getvalue()
print("Captured diff:")
print(diff_output)# Path processing types
PathLike = Union[str, Path]
PathSpec = pathspec.PathSpec
Pattern = re.Pattern[str]
# File processing
FileList = set[Path]
SourcePaths = tuple[str, ...]
# Configuration
ConfigDict = dict[str, Any]
LineRange = tuple[int, int]
LineRanges = Collection[LineRange]Install with Tessl CLI
npx tessl i tessl/pypi-black