Run Black on Python code blocks in documentation files.
npx @tessl/cli install tessl/pypi-blacken-docs@1.19.00
# blacken-docs
1
2
A 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.
3
4
## Package Information
5
6
- **Package Name**: blacken-docs
7
- **Language**: Python
8
- **Installation**: `pip install blacken-docs`
9
- **Python Support**: 3.9 to 3.13
10
- **Black Compatibility**: 22.1.0+
11
12
## Core Imports
13
14
```python
15
import blacken_docs
16
```
17
18
For programmatic use:
19
20
```python
21
from blacken_docs import format_str, format_file, main, CodeBlockError
22
```
23
24
## Basic Usage
25
26
### Command Line Usage
27
28
Process single files:
29
30
```bash
31
blacken-docs README.rst
32
blacken-docs docs/api.md
33
```
34
35
Process multiple files with glob patterns:
36
37
```bash
38
git ls-files -z -- '*.md' | xargs -0 blacken-docs
39
git ls-files -z -- '*.rst' | xargs -0 blacken-docs
40
```
41
42
### Programmatic Usage
43
44
```python
45
import blacken_docs
46
import black
47
48
# Format code blocks in a string
49
content = '''
50
```python
51
def hello( ):
52
print( "hello world" )
53
```
54
'''
55
56
# Both black.Mode and black.FileMode work (they are equivalent)
57
black_mode = black.Mode(line_length=88)
58
formatted_content, errors = blacken_docs.format_str(content, black_mode)
59
60
# Format code blocks in a file
61
exit_code = blacken_docs.format_file(
62
filename="README.md",
63
black_mode=black_mode,
64
skip_errors=False,
65
rst_literal_blocks=False,
66
check_only=False
67
)
68
```
69
70
## Capabilities
71
72
### String Formatting
73
74
Formats Python code blocks within a string using Black formatter.
75
76
```python { .api }
77
def format_str(
78
src: str,
79
black_mode: black.FileMode,
80
*,
81
rst_literal_blocks: bool = False
82
) -> tuple[str, Sequence[CodeBlockError]]:
83
"""
84
Format Python code blocks within documentation text.
85
86
Parameters:
87
- src: str - Input documentation text containing code blocks
88
- black_mode: black.FileMode - Black formatting configuration
89
- rst_literal_blocks: bool - Whether to format reStructuredText literal blocks (default: False)
90
91
Returns:
92
- tuple[str, Sequence[CodeBlockError]] - Formatted text and any parsing errors
93
"""
94
```
95
96
### File Formatting
97
98
Formats Python code blocks in documentation files and writes changes back to disk.
99
100
```python { .api }
101
def format_file(
102
filename: str,
103
black_mode: black.FileMode,
104
skip_errors: bool,
105
rst_literal_blocks: bool,
106
check_only: bool
107
) -> int:
108
"""
109
Format Python code blocks in a documentation file.
110
111
Parameters:
112
- filename: str - Path to documentation file to format
113
- black_mode: black.FileMode - Black formatting configuration
114
- skip_errors: bool - Whether to skip syntax errors and continue processing
115
- rst_literal_blocks: bool - Whether to format reStructuredText literal blocks
116
- check_only: bool - Whether to only check without modifying the file
117
118
Returns:
119
- int - Exit code (0=no changes needed, 1=changes made, 2=errors occurred)
120
"""
121
```
122
123
### Command Line Interface
124
125
Main entry point for command-line usage supporting all Black formatting options plus additional features.
126
127
```python { .api }
128
def main(argv: Sequence[str] | None = None) -> int:
129
"""
130
Main CLI function that processes files with Python code blocks.
131
132
Parameters:
133
- argv: Sequence[str] | None - Command line arguments (uses sys.argv if None)
134
135
Returns:
136
- int - Exit code for the operation
137
"""
138
```
139
140
### Error Handling
141
142
Error class for capturing code block parsing failures with location information.
143
144
```python { .api }
145
class CodeBlockError:
146
"""Error information for code blocks that failed to parse or format."""
147
148
def __init__(self, offset: int, exc: Exception) -> None:
149
"""
150
Initialize error with location and exception details.
151
152
Parameters:
153
- offset: int - Character offset in source where error occurred
154
- exc: Exception - The underlying parsing or formatting exception
155
"""
156
157
offset: int # Character position where error occurred
158
exc: Exception # The underlying exception
159
```
160
161
## Supported Documentation Formats
162
163
### Markdown
164
165
Python code blocks:
166
```markdown
167
```python
168
def example():
169
print("Hello, world!")
170
```
171
```
172
173
Python console blocks:
174
```markdown
175
```pycon
176
>>> def example():
177
... print("Hello, world!")
178
...
179
>>> example()
180
Hello, world!
181
```
182
```
183
184
Control formatting with comments:
185
```markdown
186
<!-- blacken-docs:off -->
187
```python
188
# This code block will not be formatted
189
def example( ):
190
pass
191
```
192
<!-- blacken-docs:on -->
193
```
194
195
### reStructuredText
196
197
Python code blocks:
198
```rst
199
.. code-block:: python
200
201
def example():
202
print("Hello, world!")
203
```
204
205
Python console blocks:
206
```rst
207
.. code-block:: pycon
208
209
>>> def example():
210
... print("Hello, world!")
211
...
212
```
213
214
Literal blocks (with `--rst-literal-blocks` flag):
215
```rst
216
An example::
217
218
def example():
219
print("Hello, world!")
220
```
221
222
Control formatting with comments:
223
```rst
224
.. blacken-docs:off
225
226
.. code-block:: python
227
228
# This code block will not be formatted
229
def example( ):
230
pass
231
232
.. blacken-docs:on
233
```
234
235
### LaTeX
236
237
Minted Python blocks:
238
```latex
239
\begin{minted}{python}
240
def example():
241
print("Hello, world!")
242
\end{minted}
243
```
244
245
Minted Python console blocks:
246
```latex
247
\begin{minted}{pycon}
248
>>> def example():
249
... print("Hello, world!")
250
...
251
\end{minted}
252
```
253
254
PythonTeX blocks:
255
```latex
256
\begin{pycode}
257
def example():
258
print("Hello, world!")
259
\end{pycode}
260
```
261
262
Control formatting with comments:
263
```latex
264
% blacken-docs:off
265
\begin{minted}{python}
266
# This code block will not be formatted
267
def example( ):
268
pass
269
\end{minted}
270
% blacken-docs:on
271
```
272
273
## Command Line Options
274
275
### Black Pass-through Options
276
277
These options are passed directly to Black for code formatting:
278
279
- `-l, --line-length INT` - Set maximum line length (default: 88)
280
- `--preview` - Enable Black's preview features
281
- `-S, --skip-string-normalization` - Skip string quote normalization
282
- `-t, --target-version {py39,py310,py311,py312,py313}` - Set Python target version
283
- `--pyi` - Format as Python stub (.pyi) file
284
285
### blacken-docs Specific Options
286
287
- `--check` - Check-only mode: don't modify files, just report if changes needed
288
- `-E, --skip-errors` - Skip syntax errors from Black and continue processing
289
- `--rst-literal-blocks` - Also format literal blocks in reStructuredText files
290
291
### Examples
292
293
Format with custom line length:
294
```bash
295
blacken-docs -l 120 README.md
296
```
297
298
Check mode (don't modify files):
299
```bash
300
blacken-docs --check docs/*.rst
301
```
302
303
Skip errors and format literal blocks:
304
```bash
305
blacken-docs -E --rst-literal-blocks documentation.rst
306
```
307
308
Target specific Python version:
309
```bash
310
blacken-docs -t py311 examples.md
311
```
312
313
## Integration Patterns
314
315
### Pre-commit Hook
316
317
Add to `.pre-commit-config.yaml`:
318
319
```yaml
320
repos:
321
- repo: https://github.com/adamchainz/blacken-docs
322
rev: "1.19.1" # Use the latest version
323
hooks:
324
- id: blacken-docs
325
additional_dependencies:
326
- black==24.8.0 # Pin Black version
327
```
328
329
### Programmatic Integration
330
331
```python
332
import blacken_docs
333
import black
334
from pathlib import Path
335
336
def format_documentation_files(file_paths, line_length=88):
337
"""Format Python code blocks in multiple documentation files."""
338
# Note: black.Mode is used internally by blacken-docs
339
black_mode = black.Mode(line_length=line_length)
340
results = {}
341
342
for file_path in file_paths:
343
try:
344
exit_code = blacken_docs.format_file(
345
filename=str(file_path),
346
black_mode=black_mode,
347
skip_errors=False,
348
rst_literal_blocks=True,
349
check_only=False
350
)
351
results[file_path] = {
352
'success': exit_code != 2,
353
'modified': exit_code == 1
354
}
355
except Exception as e:
356
results[file_path] = {'success': False, 'error': str(e)}
357
358
return results
359
360
# Usage
361
doc_files = Path('docs').glob('*.md')
362
results = format_documentation_files(doc_files)
363
```
364
365
## Types
366
367
```python { .api }
368
# From black module (external dependency)
369
class black.FileMode:
370
"""Black formatting configuration."""
371
def __init__(
372
self,
373
target_versions: set[TargetVersion] = ...,
374
line_length: int = 88,
375
string_normalization: bool = True,
376
is_pyi: bool = False,
377
preview: bool = False
378
): ...
379
380
# Note: black.Mode is an alias for black.FileMode used in implementation
381
black.Mode = black.FileMode
382
383
class black.TargetVersion:
384
"""Python version targets for Black formatting."""
385
PY39: TargetVersion
386
PY310: TargetVersion
387
PY311: TargetVersion
388
PY312: TargetVersion
389
PY313: TargetVersion
390
391
# Standard library types used
392
from collections.abc import Sequence
393
```
394
395
## Error Cases
396
397
Common error scenarios and handling:
398
399
- **Syntax Errors**: Invalid Python code in blocks raises exceptions, captured in CodeBlockError objects
400
- **File Not Found**: format_file raises standard Python file exceptions
401
- **Permission Errors**: File write permissions cause standard I/O exceptions
402
- **Black Formatting Errors**: Internal Black errors are captured and reported with line numbers
403
404
Use `skip_errors=True` in format_file or `-E` flag to continue processing despite syntax errors in code blocks.