Apply Black formatting only in regions changed since last commit
npx @tessl/cli install tessl/pypi-darker@3.0.00
# Darker
1
2
Darker is a Python utility that reformats Python source code files by comparing an old revision of the source tree to a newer revision, applying formatting only to regions that have changed. This selective approach allows teams to adopt code formatters incrementally without disrupting unchanged code.
3
4
## Package Information
5
6
- **Package Name**: darker
7
- **Language**: Python
8
- **Installation**: `pip install darker`
9
- **Optional Dependencies**:
10
- `pip install darker[black]` - for Black formatting
11
- `pip install darker[ruff]` - for Ruff formatting
12
- `pip install darker[pyupgrade]` - for Python syntax upgrading
13
- `pip install darker[flynt]` - for f-string conversion
14
- `pip install darker[isort]` - for import sorting
15
- `pip install darker[color]` - for colored output
16
17
## Core Imports
18
19
```python
20
import darker
21
from darker import main, format_edited_parts
22
from darker.command_line import parse_command_line
23
from darker.config import DarkerConfig, OutputMode
24
```
25
26
For formatter integration:
27
28
```python
29
from darker.formatters import create_formatter, get_formatter_names
30
from darker.formatters.base_formatter import BaseFormatter
31
```
32
33
## Basic Usage
34
35
### Command Line Usage
36
37
```bash
38
# Format only changed regions since last commit
39
darker --check --diff myproject.py
40
41
# Format multiple files with Black and isort
42
darker --black --isort src/
43
44
# Format with Ruff formatter
45
darker --formatter=ruff --check --diff .
46
47
# Format changes since specific revision
48
darker --revision=HEAD~2 --diff src/
49
```
50
51
### Programmatic Usage
52
53
```python
54
from darker import format_edited_parts
55
from darker.config import OutputMode, Exclusions
56
from darkgraylib.git import RevisionRange
57
from pathlib import Path
58
59
# Format edited parts of files
60
results = format_edited_parts(
61
common_root=Path("."),
62
paths={Path("myfile.py")},
63
exclusions=Exclusions(),
64
revrange=RevisionRange.parse_with_common_ancestor("HEAD", ":WORKTREE:"),
65
formatter=create_formatter("black"),
66
report_unmodified=False,
67
workers=1
68
)
69
70
for path, old_content, new_content in results:
71
if old_content != new_content:
72
print(f"Formatted {path}")
73
```
74
75
## Architecture
76
77
Darker is built around several key components:
78
79
- **Main Entry Point**: The `main()` function orchestrates the entire formatting process
80
- **Git Integration**: Compares revisions to identify changed regions using `EditedLinenumsDiffer`
81
- **Formatter Plugin System**: Pluggable formatters (Black, Ruff, Pyupgrade, None) via entry points
82
- **Diff Processing**: Selects which chunks to format based on Git changes
83
- **AST Verification**: Ensures formatting preserves code semantics
84
- **Configuration Management**: Handles command-line args and config file options
85
86
## Capabilities
87
88
### Main Functions
89
90
Core entry points for running darker formatting operations, including the main function that orchestrates the entire process and utility functions for file modification and output.
91
92
```python { .api }
93
def main(argv: List[str] = None) -> int: ...
94
95
def main_with_error_handling() -> int: ...
96
97
def format_edited_parts(
98
root: Path,
99
changed_files: Collection[Path],
100
exclude: Exclusions,
101
revrange: RevisionRange,
102
formatter: BaseFormatter,
103
report_unmodified: bool,
104
workers: int = 1,
105
) -> Generator[Tuple[Path, TextDocument, TextDocument], None, None]: ...
106
```
107
108
[Main Functions](./main-functions.md)
109
110
### Command Line Interface
111
112
Command line argument parsing, configuration management, and option validation for the darker command-line tool.
113
114
```python { .api }
115
def make_argument_parser(require_src: bool) -> ArgumentParser: ...
116
117
def parse_command_line(
118
argv: Optional[List[str]]
119
) -> Tuple[Namespace, DarkerConfig, DarkerConfig]: ...
120
```
121
122
[Command Line Interface](./command-line.md)
123
124
### Configuration Management
125
126
Configuration classes, validation functions, and output mode management for controlling darker's behavior through config files and command-line options.
127
128
```python { .api }
129
class DarkerConfig(BaseConfig, total=False):
130
check: bool
131
diff: bool
132
flynt: bool
133
isort: bool
134
line_length: int
135
lint: list[str]
136
skip_magic_trailing_comma: bool
137
skip_string_normalization: bool
138
target_version: str
139
formatter: str
140
141
class OutputMode:
142
NOTHING = "NOTHING"
143
DIFF = "DIFF"
144
CONTENT = "CONTENT"
145
146
def validate_config_output_mode(
147
diff: bool,
148
stdout: bool
149
) -> OutputMode: ...
150
```
151
152
[Configuration Management](./configuration.md)
153
154
### Formatter System
155
156
Pluggable formatter system supporting multiple code formatters like Black, Ruff, and Pyupgrade through a common interface and entry point system.
157
158
```python { .api }
159
def create_formatter(name: str) -> BaseFormatter: ...
160
161
def get_formatter_names() -> list[str]: ...
162
163
class BaseFormatter(ABC):
164
name: str
165
preserves_ast: bool
166
config_section: str
167
168
def read_config(self, src: tuple[str, ...], args: Namespace) -> None: ...
169
def run(self, content: TextDocument, path_from_cwd: Path) -> TextDocument: ...
170
```
171
172
[Formatter System](./formatters.md)
173
174
### Git Integration
175
176
Git repository interaction, revision comparison, and modified file discovery for determining which files and regions need formatting.
177
178
```python { .api }
179
def git_is_repository(path: Path) -> bool: ...
180
181
def git_get_modified_python_files(
182
paths: Collection[str],
183
revrange: RevisionRange,
184
cwd: Path,
185
) -> Set[str]: ...
186
187
class EditedLinenumsDiffer:
188
def __init__(self, root: Path, revrange: RevisionRange): ...
189
def revision_vs_lines(
190
self, path_in_repo: Path, content: TextDocument, context_lines: int
191
) -> List[int]: ...
192
```
193
194
[Git Integration](./git-integration.md)
195
196
### Pre-processors
197
198
Integration with code pre-processors like isort for import sorting and flynt for f-string conversion, applied before main formatting.
199
200
```python { .api }
201
def apply_isort(
202
content: TextDocument,
203
src: Tuple[str, ...],
204
config: str,
205
) -> TextDocument: ...
206
207
def apply_flynt(
208
content: TextDocument,
209
line_length: int,
210
) -> TextDocument: ...
211
```
212
213
[Pre-processors](./preprocessors.md)
214
215
## Types
216
217
### Core Types
218
219
```python { .api }
220
from typing import Tuple, List, Set, Collection, Generator, Optional
221
from pathlib import Path
222
from darkgraylib.utils import TextDocument
223
from darkgraylib.git import RevisionRange
224
225
# Type aliases
226
ProcessedDocument = Tuple[Path, TextDocument, TextDocument]
227
228
class Exclusions:
229
"""File exclusion patterns for pre-processing steps"""
230
formatter: Set[str]
231
isort: Set[str]
232
flynt: Set[str]
233
234
class TargetVersion(Enum):
235
"""Python version targets for formatting"""
236
PY33 = (3, 3)
237
PY34 = (3, 4)
238
# ... up to PY313
239
```
240
241
### Exception Types
242
243
```python { .api }
244
class DependencyError(Exception):
245
"""Parent class for dependency problems"""
246
247
class IncompatiblePackageError(DependencyError):
248
"""Incompatible version of required package"""
249
250
class MissingPackageError(DependencyError):
251
"""Required/optional package is missing"""
252
253
class NotEquivalentError(Exception):
254
"""Two ASTs being compared are not equivalent"""
255
```