0
# Pre-processors
1
2
Integration with code pre-processors like isort for import sorting and flynt for f-string conversion, applied before main formatting.
3
4
## Capabilities
5
6
### Import Sorting with isort
7
8
Integration with isort for organizing and sorting Python import statements.
9
10
```python { .api }
11
def apply_isort(
12
content: TextDocument,
13
src: Tuple[str, ...],
14
config: str,
15
) -> TextDocument:
16
"""
17
Run isort on Python source file content to organize imports.
18
19
Parameters:
20
- content: Source code content to process
21
- src: Source file/directory paths for configuration context
22
- config: Path to configuration file
23
24
Returns:
25
Source code with sorted and organized imports
26
27
Note:
28
Requires isort to be installed. If not available, returns content unchanged.
29
"""
30
31
isort: Optional[ModuleType]
32
"""
33
Optional isort module reference.
34
None if isort is not installed, otherwise the imported isort module.
35
"""
36
37
class IsortArgs(TypedDict, total=False):
38
"""Command line arguments for isort configuration."""
39
profile: str # isort profile (e.g., "black")
40
multi_line_output: int # Multi-line import output mode
41
line_length: int # Maximum line length
42
known_first_party: List[str] # First-party package names
43
known_third_party: List[str] # Third-party package names
44
force_single_line: bool # Force single-line imports
45
combine_as_imports: bool # Combine 'import x' and 'from x import y'
46
include_trailing_comma: bool # Add trailing comma to multi-line imports
47
```
48
49
### F-string Conversion with flynt
50
51
Integration with flynt for converting old-style string formatting to f-strings.
52
53
```python { .api }
54
def apply_flynt(
55
content: TextDocument,
56
line_length: int,
57
) -> TextDocument:
58
"""
59
Run flynt on Python source file content to convert to f-strings.
60
61
Converts old-style % formatting and .format() calls to f-string literals
62
where possible and safe.
63
64
Parameters:
65
- content: Source code content to process
66
- line_length: Maximum line length for formatting decisions
67
68
Returns:
69
Source code with f-string conversions applied
70
71
Note:
72
Requires flynt to be installed. If not available, returns content unchanged.
73
"""
74
75
flynt: Optional[ModuleType]
76
"""
77
Optional flynt module reference.
78
None if flynt is not installed, otherwise the imported flynt module.
79
"""
80
```
81
82
## Usage Examples
83
84
### Basic Import Sorting
85
86
```python
87
from darker.import_sorting import apply_isort, isort
88
from darkgraylib.utils import TextDocument
89
90
# Check if isort is available
91
if isort is not None:
92
print("isort is available for import sorting")
93
else:
94
print("isort not installed - install with: pip install darker[isort]")
95
96
# Apply import sorting
97
source_code = TextDocument.from_lines([
98
"import os",
99
"from typing import List",
100
"import sys",
101
"from mypackage import mymodule",
102
"",
103
"def main():",
104
" pass"
105
])
106
107
sorted_code = apply_isort(
108
content=source_code,
109
src=("src/",),
110
config="pyproject.toml"
111
)
112
113
print("Sorted imports:")
114
print(sorted_code.string)
115
```
116
117
### F-string Conversion
118
119
```python
120
from darker.fstring import apply_flynt, flynt
121
from darkgraylib.utils import TextDocument
122
123
# Check if flynt is available
124
if flynt is not None:
125
print("flynt is available for f-string conversion")
126
else:
127
print("flynt not installed - install with: pip install darker[flynt]")
128
129
# Apply f-string conversion
130
source_code = TextDocument.from_lines([
131
'name = "World"',
132
'message = "Hello, {}!".format(name)',
133
'count = 42',
134
'status = "Processing %d items" % count',
135
'',
136
'print(message)',
137
'print(status)'
138
])
139
140
converted_code = apply_flynt(
141
content=source_code,
142
line_length=88
143
)
144
145
print("Converted to f-strings:")
146
print(converted_code.string)
147
# Expected output would show f-string conversions:
148
# message = f"Hello, {name}!"
149
# status = f"Processing {count} items"
150
```
151
152
### Integration with Main Workflow
153
154
```python
155
from darker.import_sorting import apply_isort
156
from darker.fstring import apply_flynt
157
from darker.formatters import create_formatter
158
from darkgraylib.utils import TextDocument
159
160
def apply_all_preprocessing(
161
content: TextDocument,
162
use_isort: bool = True,
163
use_flynt: bool = True,
164
line_length: int = 88
165
) -> TextDocument:
166
"""Apply all pre-processing steps in the correct order."""
167
168
result = content
169
170
# Step 1: Sort imports (if enabled)
171
if use_isort:
172
result = apply_isort(
173
content=result,
174
src=("src/",),
175
config="pyproject.toml"
176
)
177
178
# Step 2: Convert to f-strings (if enabled)
179
if use_flynt:
180
result = apply_flynt(
181
content=result,
182
line_length=line_length
183
)
184
185
# Step 3: Apply main formatter (Black, Ruff, etc.)
186
formatter = create_formatter("black")
187
config = formatter.read_config(("src/",), "pyproject.toml")
188
result = formatter.run(result, config)
189
190
return result
191
192
# Usage
193
source = TextDocument.from_lines([
194
"import sys",
195
"from typing import Dict",
196
"import os",
197
"",
198
"def greet(name):",
199
' return "Hello {}".format(name)',
200
])
201
202
processed = apply_all_preprocessing(source)
203
print(processed.string)
204
```
205
206
### Configuration Examples
207
208
#### pyproject.toml Configuration
209
210
```toml
211
# isort configuration
212
[tool.isort]
213
profile = "black"
214
multi_line_output = 3
215
line_length = 88
216
known_first_party = ["mypackage"]
217
known_third_party = ["requests", "pandas"]
218
include_trailing_comma = true
219
force_grid_wrap = 0
220
use_parentheses = true
221
ensure_newline_before_comments = true
222
223
# flynt configuration (if supported by your version)
224
[tool.flynt]
225
line_length = 88
226
```
227
228
#### Programmatic Configuration
229
230
```python
231
from darker.import_sorting import IsortArgs
232
233
# Configure isort programmatically
234
isort_config: IsortArgs = {
235
'profile': 'black',
236
'multi_line_output': 3,
237
'line_length': 88,
238
'known_first_party': ['mypackage', 'myutil'],
239
'known_third_party': ['requests', 'click'],
240
'force_single_line': False,
241
'combine_as_imports': True,
242
'include_trailing_comma': True,
243
}
244
245
# This would typically be handled by apply_isort reading from config file
246
```
247
248
### Error Handling and Optional Dependencies
249
250
```python
251
from darker.import_sorting import apply_isort, isort
252
from darker.fstring import apply_flynt, flynt
253
from darkgraylib.utils import TextDocument
254
255
def safe_preprocess(content: TextDocument) -> TextDocument:
256
"""Safely apply preprocessing with fallbacks for missing dependencies."""
257
258
result = content
259
260
# Try isort if available
261
try:
262
if isort is not None:
263
result = apply_isort(result, ("src/",), "pyproject.toml")
264
print("Applied isort import sorting")
265
else:
266
print("Skipping isort - not installed")
267
except Exception as e:
268
print(f"isort failed: {e}")
269
270
# Try flynt if available
271
try:
272
if flynt is not None:
273
result = apply_flynt(result, 88)
274
print("Applied flynt f-string conversion")
275
else:
276
print("Skipping flynt - not installed")
277
except Exception as e:
278
print(f"flynt failed: {e}")
279
280
return result
281
282
# Usage
283
source = TextDocument.from_str("import sys\nimport os\n")
284
processed = safe_preprocess(source)
285
```
286
287
### Custom Pre-processor Integration
288
289
```python
290
from darkgraylib.utils import TextDocument
291
from typing import Callable
292
293
PreprocessorFunc = Callable[[TextDocument], TextDocument]
294
295
def create_preprocessing_pipeline(*preprocessors: PreprocessorFunc) -> PreprocessorFunc:
296
"""Create a pipeline of preprocessing functions."""
297
298
def pipeline(content: TextDocument) -> TextDocument:
299
result = content
300
for preprocessor in preprocessors:
301
try:
302
result = preprocessor(result)
303
except Exception as e:
304
print(f"Preprocessor {preprocessor.__name__} failed: {e}")
305
return result
306
307
return pipeline
308
309
# Example usage
310
def my_custom_preprocessor(content: TextDocument) -> TextDocument:
311
"""Custom preprocessing step."""
312
lines = [line.rstrip() for line in content.lines] # Remove trailing whitespace
313
return TextDocument.from_lines(lines)
314
315
# Create pipeline
316
pipeline = create_preprocessing_pipeline(
317
lambda c: apply_isort(c, ("src/",), "pyproject.toml"),
318
my_custom_preprocessor,
319
lambda c: apply_flynt(c, 88),
320
)
321
322
# Apply pipeline
323
result = pipeline(source_content)
324
```