Run Black on Python code blocks in documentation files.
npx @tessl/cli install tessl/pypi-blacken-docs@1.19.0A command-line tool that runs Black on Python code blocks in documentation files. It automatically formats Python code examples in Markdown, reStructuredText, and LaTeX files, helping maintain consistent code style across technical documentation, tutorials, and API documentation.
pip install blacken-docsimport blacken_docsFor programmatic use:
from blacken_docs import format_str, format_file, main, CodeBlockErrorProcess single files:
blacken-docs README.rst
blacken-docs docs/api.mdProcess multiple files with glob patterns:
git ls-files -z -- '*.md' | xargs -0 blacken-docs
git ls-files -z -- '*.rst' | xargs -0 blacken-docsimport blacken_docs
import black
# Format code blocks in a string
content = '''
```python
def hello( ):
print( "hello world" )'''
black_mode = black.Mode(line_length=88) formatted_content, errors = blacken_docs.format_str(content, black_mode)
exit_code = blacken_docs.format_file( filename="README.md", black_mode=black_mode, skip_errors=False, rst_literal_blocks=False, check_only=False )
## Capabilities
### String Formatting
Formats Python code blocks within a string using Black formatter.
```python { .api }
def format_str(
src: str,
black_mode: black.FileMode,
*,
rst_literal_blocks: bool = False
) -> tuple[str, Sequence[CodeBlockError]]:
"""
Format Python code blocks within documentation text.
Parameters:
- src: str - Input documentation text containing code blocks
- black_mode: black.FileMode - Black formatting configuration
- rst_literal_blocks: bool - Whether to format reStructuredText literal blocks (default: False)
Returns:
- tuple[str, Sequence[CodeBlockError]] - Formatted text and any parsing errors
"""Formats Python code blocks in documentation files and writes changes back to disk.
def format_file(
filename: str,
black_mode: black.FileMode,
skip_errors: bool,
rst_literal_blocks: bool,
check_only: bool
) -> int:
"""
Format Python code blocks in a documentation file.
Parameters:
- filename: str - Path to documentation file to format
- black_mode: black.FileMode - Black formatting configuration
- skip_errors: bool - Whether to skip syntax errors and continue processing
- rst_literal_blocks: bool - Whether to format reStructuredText literal blocks
- check_only: bool - Whether to only check without modifying the file
Returns:
- int - Exit code (0=no changes needed, 1=changes made, 2=errors occurred)
"""Main entry point for command-line usage supporting all Black formatting options plus additional features.
def main(argv: Sequence[str] | None = None) -> int:
"""
Main CLI function that processes files with Python code blocks.
Parameters:
- argv: Sequence[str] | None - Command line arguments (uses sys.argv if None)
Returns:
- int - Exit code for the operation
"""Error class for capturing code block parsing failures with location information.
class CodeBlockError:
"""Error information for code blocks that failed to parse or format."""
def __init__(self, offset: int, exc: Exception) -> None:
"""
Initialize error with location and exception details.
Parameters:
- offset: int - Character offset in source where error occurred
- exc: Exception - The underlying parsing or formatting exception
"""
offset: int # Character position where error occurred
exc: Exception # The underlying exceptionPython code blocks:
```python
def example():
print("Hello, world!")Python console blocks:
```markdown
```pycon
>>> def example():
... print("Hello, world!")
...
>>> example()
Hello, world!Control formatting with comments:
```markdown
<!-- blacken-docs:off -->
```python
# This code block will not be formatted
def example( ):
pass### reStructuredText
Python code blocks:
```rst
.. code-block:: python
def example():
print("Hello, world!")Python console blocks:
.. code-block:: pycon
>>> def example():
... print("Hello, world!")
...Literal blocks (with --rst-literal-blocks flag):
An example::
def example():
print("Hello, world!")Control formatting with comments:
.. blacken-docs:off
.. code-block:: python
# This code block will not be formatted
def example( ):
pass
.. blacken-docs:onMinted Python blocks:
\begin{minted}{python}
def example():
print("Hello, world!")
\end{minted}Minted Python console blocks:
\begin{minted}{pycon}
>>> def example():
... print("Hello, world!")
...
\end{minted}PythonTeX blocks:
\begin{pycode}
def example():
print("Hello, world!")
\end{pycode}Control formatting with comments:
% blacken-docs:off
\begin{minted}{python}
# This code block will not be formatted
def example( ):
pass
\end{minted}
% blacken-docs:onThese options are passed directly to Black for code formatting:
-l, --line-length INT - Set maximum line length (default: 88)--preview - Enable Black's preview features-S, --skip-string-normalization - Skip string quote normalization-t, --target-version {py39,py310,py311,py312,py313} - Set Python target version--pyi - Format as Python stub (.pyi) file--check - Check-only mode: don't modify files, just report if changes needed-E, --skip-errors - Skip syntax errors from Black and continue processing--rst-literal-blocks - Also format literal blocks in reStructuredText filesFormat with custom line length:
blacken-docs -l 120 README.mdCheck mode (don't modify files):
blacken-docs --check docs/*.rstSkip errors and format literal blocks:
blacken-docs -E --rst-literal-blocks documentation.rstTarget specific Python version:
blacken-docs -t py311 examples.mdAdd to .pre-commit-config.yaml:
repos:
- repo: https://github.com/adamchainz/blacken-docs
rev: "1.19.1" # Use the latest version
hooks:
- id: blacken-docs
additional_dependencies:
- black==24.8.0 # Pin Black versionimport blacken_docs
import black
from pathlib import Path
def format_documentation_files(file_paths, line_length=88):
"""Format Python code blocks in multiple documentation files."""
# Note: black.Mode is used internally by blacken-docs
black_mode = black.Mode(line_length=line_length)
results = {}
for file_path in file_paths:
try:
exit_code = blacken_docs.format_file(
filename=str(file_path),
black_mode=black_mode,
skip_errors=False,
rst_literal_blocks=True,
check_only=False
)
results[file_path] = {
'success': exit_code != 2,
'modified': exit_code == 1
}
except Exception as e:
results[file_path] = {'success': False, 'error': str(e)}
return results
# Usage
doc_files = Path('docs').glob('*.md')
results = format_documentation_files(doc_files)# From black module (external dependency)
class black.FileMode:
"""Black formatting configuration."""
def __init__(
self,
target_versions: set[TargetVersion] = ...,
line_length: int = 88,
string_normalization: bool = True,
is_pyi: bool = False,
preview: bool = False
): ...
# Note: black.Mode is an alias for black.FileMode used in implementation
black.Mode = black.FileMode
class black.TargetVersion:
"""Python version targets for Black formatting."""
PY39: TargetVersion
PY310: TargetVersion
PY311: TargetVersion
PY312: TargetVersion
PY313: TargetVersion
# Standard library types used
from collections.abc import SequenceCommon error scenarios and handling:
Use skip_errors=True in format_file or -E flag to continue processing despite syntax errors in code blocks.