0
# Types and Exceptions
1
2
Exception classes for error handling, constants for default configuration, type definitions, and utility functions for encoding detection and reporting functionality.
3
4
## Capabilities
5
6
### Exception Classes
7
8
Exception hierarchy for handling various error conditions during code formatting.
9
10
```python { .api }
11
class NothingChanged(UserWarning):
12
"""
13
Raised when reformatted code is identical to source.
14
15
Used to indicate that formatting was successful but no changes
16
were needed. Commonly caught and handled gracefully in tooling.
17
"""
18
pass
19
20
class InvalidInput(ValueError):
21
"""
22
Raised when source code fails all parse attempts.
23
24
Indicates that the input code contains syntax errors or uses
25
unsupported Python syntax that cannot be parsed by Black.
26
"""
27
pass
28
29
class ASTSafetyError(Exception):
30
"""
31
Raised when Black's generated code is not equivalent to source AST.
32
33
This indicates a bug in Black's formatting logic where the
34
output is not semantically equivalent to the input.
35
"""
36
pass
37
```
38
39
### Report Classes
40
41
Classes for tracking and reporting formatting results and statistics.
42
43
```python { .api }
44
class Changed(Enum):
45
"""File change status enumeration."""
46
NO = 0 # File was not changed
47
CACHED = 1 # File was not changed (result from cache)
48
YES = 2 # File was changed
49
50
@dataclass
51
class Report:
52
"""
53
Tracks formatting results and statistics.
54
55
Used by CLI and batch processing to accumulate results
56
across multiple files and provide summary statistics.
57
"""
58
check: bool = False
59
diff: bool = False
60
quiet: bool = False
61
verbose: bool = False
62
change_count: int = 0
63
same_count: int = 0
64
failure_count: int = 0
65
66
def done(self, src: Path, changed: Changed) -> None:
67
"""
68
Record completion of file processing.
69
70
Parameters:
71
- src: Path of processed file
72
- changed: Whether file was modified
73
"""
74
75
def failed(self, src: Path, message: str) -> None:
76
"""
77
Record processing failure.
78
79
Parameters:
80
- src: Path of failed file
81
- message: Error message
82
"""
83
84
def path_ignored(self, path: Path, message: str) -> None:
85
"""
86
Record ignored path with reason.
87
88
Parameters:
89
- path: Ignored path
90
- message: Reason for ignoring
91
"""
92
93
@property
94
def return_code(self) -> int:
95
"""
96
Calculate exit code for CLI usage.
97
98
Returns:
99
- 0: Success (no changes needed or changes made successfully)
100
- 1: Changes needed (when check=True) or formatting errors
101
"""
102
```
103
104
### Cache Management
105
106
Classes for managing formatting result caching to improve performance.
107
108
```python { .api }
109
@dataclass
110
class Cache:
111
"""
112
File formatting cache management.
113
114
Caches formatting results based on file content hash and
115
configuration to avoid re-formatting unchanged files.
116
"""
117
mode: Mode
118
cache_file: Path
119
file_data: dict[str, FileData] = field(default_factory=dict)
120
121
@classmethod
122
def read(cls, mode: Mode) -> 'Cache':
123
"""
124
Read existing cache from disk.
125
126
Parameters:
127
- mode: Mode configuration for cache key generation
128
129
Returns:
130
Cache instance loaded from disk or new empty cache
131
"""
132
133
@staticmethod
134
def hash_digest(path: Path) -> str:
135
"""
136
Generate hash digest for file content.
137
138
Parameters:
139
- path: Path to file
140
141
Returns:
142
SHA-256 hash of file content and metadata
143
"""
144
145
@staticmethod
146
def get_file_data(path: Path) -> 'FileData':
147
"""
148
Get file metadata for caching.
149
150
Parameters:
151
- path: Path to file
152
153
Returns:
154
FileData with hash and modification time
155
"""
156
157
@dataclass
158
class FileData:
159
"""File metadata for cache entries."""
160
hash: str
161
mtime: float
162
```
163
164
### Output Functions
165
166
Functions for formatted output with styling support.
167
168
```python { .api }
169
def out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None:
170
"""
171
Standard output with optional styling.
172
173
Parameters:
174
- message: Message to output (None for newline only)
175
- nl: Add newline after message
176
- **styles: Click styling options (fg, bg, bold, etc.)
177
"""
178
179
def err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None:
180
"""
181
Error output with optional styling.
182
183
Parameters:
184
- message: Error message to output
185
- nl: Add newline after message
186
- **styles: Click styling options
187
"""
188
189
def diff(a: str, b: str, a_name: str, b_name: str) -> str:
190
"""
191
Generate unified diff between strings.
192
193
Parameters:
194
- a: Original content
195
- b: Modified content
196
- a_name: Name/label for original
197
- b_name: Name/label for modified
198
199
Returns:
200
Unified diff string
201
"""
202
203
def color_diff(contents: str) -> str:
204
"""
205
Add ANSI color codes to diff output.
206
207
Parameters:
208
- contents: Plain diff text
209
210
Returns:
211
Colorized diff with ANSI escape sequences
212
"""
213
214
def ipynb_diff(a: str, b: str, a_name: str, b_name: str) -> str:
215
"""
216
Generate diff for Jupyter notebook cells.
217
218
Parameters:
219
- a: Original notebook cell content
220
- b: Modified notebook cell content
221
- a_name: Label for original
222
- b_name: Label for modified
223
224
Returns:
225
Diff formatted for notebook context
226
"""
227
228
def dump_to_file(*output: str, ensure_final_newline: bool = True) -> str:
229
"""
230
Write output to temporary file and return path.
231
232
Parameters:
233
- *output: Content strings to write
234
- ensure_final_newline: Add final newline if missing
235
236
Returns:
237
Path to created temporary file
238
"""
239
```
240
241
### Parsing Utilities
242
243
Core parsing functions and AST processing utilities.
244
245
```python { .api }
246
def lib2to3_parse(src_txt: str, target_versions: Collection[TargetVersion] = ()) -> Node:
247
"""
248
Parse Python source using lib2to3 with version targeting.
249
250
Parameters:
251
- src_txt: Python source code to parse
252
- target_versions: Python versions to attempt parsing with
253
254
Returns:
255
lib2to3 Node representing parsed AST
256
257
Raises:
258
- InvalidInput: If parsing fails for all target versions
259
"""
260
261
def parse_ast(src: str) -> ast.AST:
262
"""
263
Parse source to standard Python AST.
264
265
Parameters:
266
- src: Python source code
267
268
Returns:
269
Python ast.AST object
270
271
Raises:
272
- SyntaxError: If source contains syntax errors
273
"""
274
275
def stringify_ast(node: ast.AST) -> Iterator[str]:
276
"""
277
Convert AST to comparable string representation.
278
279
Parameters:
280
- node: AST node to stringify
281
282
Yields:
283
String tokens representing AST structure
284
"""
285
```
286
287
## Constants
288
289
### Default Configuration Values
290
291
```python { .api }
292
# Line length default
293
DEFAULT_LINE_LENGTH: int = 88
294
295
# File pattern constants
296
DEFAULT_EXCLUDES: str = r"/(\.direnv|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|venv|\.svn|\.ipynb_checkpoints|_build|buck-out|build|dist)/"
297
298
DEFAULT_INCLUDES: str = r"(\.pyi?|\.ipynb)$"
299
300
# Stdin placeholder for file processing
301
STDIN_PLACEHOLDER: str = "__BLACK_STDIN_FILENAME__"
302
```
303
304
### Type Aliases
305
306
```python { .api }
307
# Content types
308
FileContent = str
309
Encoding = str
310
NewLine = str
311
312
# Legacy compatibility
313
FileMode = Mode
314
315
# AST types (from blib2to3)
316
Node = blib2to3.pytree.Node
317
Leaf = blib2to3.pytree.Leaf
318
LN = Union[Leaf, Node] # Leaf or Node
319
NL = blib2to3.pytree.Base # Node or Leaf base
320
321
# Path types
322
PathLike = Union[str, Path, os.PathLike[str]]
323
324
# Collection types
325
Collection = typing.Collection
326
Sequence = typing.Sequence
327
Iterator = typing.Iterator
328
```
329
330
## Usage Examples
331
332
### Exception Handling
333
334
```python
335
import black
336
from pathlib import Path
337
338
def safe_format_file(file_path: Path) -> tuple[bool, str]:
339
"""Safely format file with comprehensive error handling."""
340
try:
341
changed = black.format_file_in_place(
342
file_path,
343
fast=False,
344
mode=black.Mode(),
345
write_back=black.WriteBack.YES
346
)
347
return changed, "Success"
348
349
except black.NothingChanged:
350
return False, "No changes needed"
351
352
except black.InvalidInput as e:
353
return False, f"Syntax error: {e}"
354
355
except black.ASTSafetyError as e:
356
return False, f"AST safety check failed: {e}"
357
358
except PermissionError:
359
return False, "Permission denied"
360
361
except Exception as e:
362
return False, f"Unexpected error: {e}"
363
364
# Usage
365
changed, message = safe_format_file(Path("example.py"))
366
print(f"Result: {message}")
367
```
368
369
### Report Usage
370
371
```python
372
from pathlib import Path
373
374
# Create report for batch processing
375
report = black.Report(check=False, diff=True, verbose=True)
376
377
files = [Path("file1.py"), Path("file2.py"), Path("file3.py")]
378
379
for file_path in files:
380
try:
381
changed = black.format_file_in_place(
382
file_path,
383
fast=False,
384
mode=black.Mode(),
385
write_back=black.WriteBack.YES
386
)
387
388
# Record result
389
status = black.Changed.YES if changed else black.Changed.NO
390
report.done(file_path, status)
391
392
except Exception as e:
393
report.failed(file_path, str(e))
394
395
# Print summary
396
print(f"Files changed: {report.change_count}")
397
print(f"Files unchanged: {report.same_count}")
398
print(f"Files failed: {report.failure_count}")
399
print(f"Exit code: {report.return_code}")
400
```
401
402
### Cache Usage
403
404
```python
405
# Read existing cache
406
mode = black.Mode(line_length=88)
407
cache = black.Cache.read(mode)
408
409
# Check if file needs formatting
410
file_path = Path("example.py")
411
file_data = black.Cache.get_file_data(file_path)
412
file_hash = black.Cache.hash_digest(file_path)
413
414
if file_hash in cache.file_data:
415
print("File already processed (cached)")
416
else:
417
print("File needs processing")
418
# ... format file ...
419
# Update cache with new file data
420
cache.file_data[file_hash] = file_data
421
```
422
423
### Output Functions Usage
424
425
```python
426
import black
427
428
# Styled output
429
black.out("Formatting completed successfully", fg="green", bold=True)
430
black.err("Formatting failed", fg="red", bold=True)
431
432
# Generate and display diff
433
original = "def hello(name):print(f'Hello {name}!')"
434
formatted = black.format_str(original, mode=black.Mode())
435
436
diff_text = black.diff(original, formatted, "original", "formatted")
437
colored_diff = black.color_diff(diff_text)
438
439
print("Plain diff:")
440
print(diff_text)
441
print("\nColored diff:")
442
print(colored_diff)
443
444
# Write to temporary file
445
temp_path = black.dump_to_file(formatted, ensure_final_newline=True)
446
print(f"Wrote formatted code to: {temp_path}")
447
```
448
449
### Parsing and AST Usage
450
451
```python
452
import ast
453
import black
454
455
code = """
456
def example_function(x: int, y: str = "default") -> str:
457
return f"{x}: {y}"
458
"""
459
460
# Parse with lib2to3 (Black's parser)
461
lib2to3_tree = black.lib2to3_parse(code)
462
print(f"lib2to3 tree type: {type(lib2to3_tree)}")
463
464
# Parse with standard AST
465
ast_tree = black.parse_ast(code)
466
print(f"AST tree type: {type(ast_tree)}")
467
468
# Convert AST to comparable format
469
ast_strings = list(black.stringify_ast(ast_tree))
470
print(f"AST representation: {ast_strings[:5]}...") # First 5 tokens
471
472
# Use in equivalence checking
473
formatted = black.format_str(code, mode=black.Mode())
474
try:
475
black.assert_equivalent(code, formatted)
476
print("Formatted code is AST-equivalent to original")
477
except AssertionError as e:
478
print(f"AST equivalence check failed: {e}")
479
```
480
481
### Type Checking Integration
482
483
```python
484
from typing import Union, Optional
485
import black
486
487
def format_code_safe(
488
code: str,
489
*,
490
mode: Optional[black.Mode] = None,
491
lines: Optional[black.Collection[tuple[int, int]]] = None
492
) -> Union[str, None]:
493
"""
494
Type-safe code formatting function.
495
496
Returns None if formatting fails, formatted string if successful.
497
"""
498
if mode is None:
499
mode = black.Mode()
500
501
if lines is None:
502
lines = ()
503
504
try:
505
return black.format_str(code, mode=mode, lines=lines)
506
except (black.InvalidInput, black.NothingChanged):
507
return None
508
except Exception:
509
return None
510
511
# Usage with type checking
512
result = format_code_safe("def hello(): pass")
513
if result is not None:
514
print("Formatted successfully")
515
print(result)
516
else:
517
print("Formatting failed")
518
```