Apply Black formatting only in regions changed since last commit
—
Integration with code pre-processors like isort for import sorting and flynt for f-string conversion, applied before main formatting.
Integration with isort for organizing and sorting Python import statements.
def apply_isort(
content: TextDocument,
src: Tuple[str, ...],
config: str,
) -> TextDocument:
"""
Run isort on Python source file content to organize imports.
Parameters:
- content: Source code content to process
- src: Source file/directory paths for configuration context
- config: Path to configuration file
Returns:
Source code with sorted and organized imports
Note:
Requires isort to be installed. If not available, returns content unchanged.
"""
isort: Optional[ModuleType]
"""
Optional isort module reference.
None if isort is not installed, otherwise the imported isort module.
"""
class IsortArgs(TypedDict, total=False):
"""Command line arguments for isort configuration."""
profile: str # isort profile (e.g., "black")
multi_line_output: int # Multi-line import output mode
line_length: int # Maximum line length
known_first_party: List[str] # First-party package names
known_third_party: List[str] # Third-party package names
force_single_line: bool # Force single-line imports
combine_as_imports: bool # Combine 'import x' and 'from x import y'
include_trailing_comma: bool # Add trailing comma to multi-line importsIntegration with flynt for converting old-style string formatting to f-strings.
def apply_flynt(
content: TextDocument,
line_length: int,
) -> TextDocument:
"""
Run flynt on Python source file content to convert to f-strings.
Converts old-style % formatting and .format() calls to f-string literals
where possible and safe.
Parameters:
- content: Source code content to process
- line_length: Maximum line length for formatting decisions
Returns:
Source code with f-string conversions applied
Note:
Requires flynt to be installed. If not available, returns content unchanged.
"""
flynt: Optional[ModuleType]
"""
Optional flynt module reference.
None if flynt is not installed, otherwise the imported flynt module.
"""from darker.import_sorting import apply_isort, isort
from darkgraylib.utils import TextDocument
# Check if isort is available
if isort is not None:
print("isort is available for import sorting")
else:
print("isort not installed - install with: pip install darker[isort]")
# Apply import sorting
source_code = TextDocument.from_lines([
"import os",
"from typing import List",
"import sys",
"from mypackage import mymodule",
"",
"def main():",
" pass"
])
sorted_code = apply_isort(
content=source_code,
src=("src/",),
config="pyproject.toml"
)
print("Sorted imports:")
print(sorted_code.string)from darker.fstring import apply_flynt, flynt
from darkgraylib.utils import TextDocument
# Check if flynt is available
if flynt is not None:
print("flynt is available for f-string conversion")
else:
print("flynt not installed - install with: pip install darker[flynt]")
# Apply f-string conversion
source_code = TextDocument.from_lines([
'name = "World"',
'message = "Hello, {}!".format(name)',
'count = 42',
'status = "Processing %d items" % count',
'',
'print(message)',
'print(status)'
])
converted_code = apply_flynt(
content=source_code,
line_length=88
)
print("Converted to f-strings:")
print(converted_code.string)
# Expected output would show f-string conversions:
# message = f"Hello, {name}!"
# status = f"Processing {count} items"from darker.import_sorting import apply_isort
from darker.fstring import apply_flynt
from darker.formatters import create_formatter
from darkgraylib.utils import TextDocument
def apply_all_preprocessing(
content: TextDocument,
use_isort: bool = True,
use_flynt: bool = True,
line_length: int = 88
) -> TextDocument:
"""Apply all pre-processing steps in the correct order."""
result = content
# Step 1: Sort imports (if enabled)
if use_isort:
result = apply_isort(
content=result,
src=("src/",),
config="pyproject.toml"
)
# Step 2: Convert to f-strings (if enabled)
if use_flynt:
result = apply_flynt(
content=result,
line_length=line_length
)
# Step 3: Apply main formatter (Black, Ruff, etc.)
formatter = create_formatter("black")
config = formatter.read_config(("src/",), "pyproject.toml")
result = formatter.run(result, config)
return result
# Usage
source = TextDocument.from_lines([
"import sys",
"from typing import Dict",
"import os",
"",
"def greet(name):",
' return "Hello {}".format(name)',
])
processed = apply_all_preprocessing(source)
print(processed.string)# isort configuration
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
known_first_party = ["mypackage"]
known_third_party = ["requests", "pandas"]
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
# flynt configuration (if supported by your version)
[tool.flynt]
line_length = 88from darker.import_sorting import IsortArgs
# Configure isort programmatically
isort_config: IsortArgs = {
'profile': 'black',
'multi_line_output': 3,
'line_length': 88,
'known_first_party': ['mypackage', 'myutil'],
'known_third_party': ['requests', 'click'],
'force_single_line': False,
'combine_as_imports': True,
'include_trailing_comma': True,
}
# This would typically be handled by apply_isort reading from config filefrom darker.import_sorting import apply_isort, isort
from darker.fstring import apply_flynt, flynt
from darkgraylib.utils import TextDocument
def safe_preprocess(content: TextDocument) -> TextDocument:
"""Safely apply preprocessing with fallbacks for missing dependencies."""
result = content
# Try isort if available
try:
if isort is not None:
result = apply_isort(result, ("src/",), "pyproject.toml")
print("Applied isort import sorting")
else:
print("Skipping isort - not installed")
except Exception as e:
print(f"isort failed: {e}")
# Try flynt if available
try:
if flynt is not None:
result = apply_flynt(result, 88)
print("Applied flynt f-string conversion")
else:
print("Skipping flynt - not installed")
except Exception as e:
print(f"flynt failed: {e}")
return result
# Usage
source = TextDocument.from_str("import sys\nimport os\n")
processed = safe_preprocess(source)from darkgraylib.utils import TextDocument
from typing import Callable
PreprocessorFunc = Callable[[TextDocument], TextDocument]
def create_preprocessing_pipeline(*preprocessors: PreprocessorFunc) -> PreprocessorFunc:
"""Create a pipeline of preprocessing functions."""
def pipeline(content: TextDocument) -> TextDocument:
result = content
for preprocessor in preprocessors:
try:
result = preprocessor(result)
except Exception as e:
print(f"Preprocessor {preprocessor.__name__} failed: {e}")
return result
return pipeline
# Example usage
def my_custom_preprocessor(content: TextDocument) -> TextDocument:
"""Custom preprocessing step."""
lines = [line.rstrip() for line in content.lines] # Remove trailing whitespace
return TextDocument.from_lines(lines)
# Create pipeline
pipeline = create_preprocessing_pipeline(
lambda c: apply_isort(c, ("src/",), "pyproject.toml"),
my_custom_preprocessor,
lambda c: apply_flynt(c, 88),
)
# Apply pipeline
result = pipeline(source_content)Install with Tessl CLI
npx tessl i tessl/pypi-darker@3.0.1