or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

chooser.mdcommand-line.mdconfiguration.mddiff-utilities.mdfile-utilities.mdformatters.mdgit-integration.mdindex.mdmain-functions.mdpreprocessors.mdverification.md

formatters.mddocs/

0

# Formatter System

1

2

Pluggable formatter system supporting multiple code formatters like Black, Ruff, and Pyupgrade through a common interface and entry point system.

3

4

## Capabilities

5

6

### Formatter Plugin Management

7

8

Functions for discovering and creating formatter instances.

9

10

```python { .api }

11

def get_formatter_entry_points(name: str | None = None) -> tuple[EntryPoint, ...]:

12

"""

13

Get the entry points of all built-in code re-formatter plugins.

14

15

Parameters:

16

- name: Optional formatter name to filter by

17

18

Returns:

19

Tuple of entry points for formatter plugins

20

"""

21

22

def get_formatter_names() -> list[str]:

23

"""

24

Get the names of all built-in code re-formatter plugins.

25

26

Returns:

27

List of available formatter names (e.g., ['black', 'ruff', 'pyupgrade', 'none'])

28

"""

29

30

def create_formatter(name: str) -> BaseFormatter:

31

"""

32

Create a code re-formatter plugin instance by name.

33

34

Parameters:

35

- name: Name of the formatter to create

36

37

Returns:

38

Formatter instance implementing BaseFormatter interface

39

"""

40

41

ENTRY_POINT_GROUP: str = "darker.formatter"

42

"""Entry point group name for formatter plugins."""

43

```

44

45

### Base Formatter Interface

46

47

Abstract base class and common interfaces for all formatters.

48

49

```python { .api }

50

class BaseFormatter(ABC):

51

"""

52

Abstract base class for code re-formatters.

53

54

All formatter plugins must inherit from this class and implement

55

the required methods and attributes.

56

"""

57

name: str # Human-readable formatter name

58

preserves_ast: bool # Whether formatter preserves AST

59

config_section: str # Configuration section name

60

61

def read_config(self, src: tuple[str, ...], args: Namespace) -> None:

62

"""

63

Read configuration for the formatter.

64

65

Parameters:

66

- src: Source file/directory paths for configuration context

67

- args: Command line arguments namespace

68

69

Returns:

70

None (configuration is stored in self.config)

71

"""

72

73

@abstractmethod

74

def run(self, content: TextDocument, path_from_cwd: Path) -> TextDocument:

75

"""

76

Run the formatter on the given content.

77

78

Parameters:

79

- content: Source code content to format

80

- path_from_cwd: Path to the file being formatted (relative to cwd)

81

82

Returns:

83

Formatted source code content

84

"""

85

86

def get_config_path(self, config: Any) -> Path | None:

87

"""Get configuration file path if available."""

88

89

def get_line_length(self, config: Any) -> int:

90

"""Get line length setting from configuration."""

91

92

def get_exclude(self, config: Any) -> Pattern[str]:

93

"""Get file exclusion patterns."""

94

95

def get_extend_exclude(self, config: Any) -> Pattern[str]:

96

"""Get extended file exclusion patterns."""

97

98

def get_force_exclude(self, config: Any) -> Pattern[str]:

99

"""Get forced file exclusion patterns."""

100

101

class HasConfig(Generic[ConfigT]):

102

"""

103

Generic base class for formatters with typed configuration.

104

105

Type parameter ConfigT represents the formatter's configuration type.

106

"""

107

config: ConfigT

108

```

109

110

### Built-in Formatters

111

112

Concrete formatter implementations included with darker.

113

114

```python { .api }

115

class BlackFormatter(BaseFormatter):

116

"""

117

Black code formatter plugin interface.

118

119

Integrates with Black for Python code formatting with full AST preservation.

120

"""

121

name = "black"

122

preserves_ast = True

123

config_section = "tool.black"

124

125

class RuffFormatter(BaseFormatter):

126

"""

127

Ruff code formatter plugin interface.

128

129

Integrates with Ruff's formatting capabilities for fast Python code formatting.

130

"""

131

name = "ruff format"

132

preserves_ast = True

133

config_section = "tool.ruff"

134

135

class PyupgradeFormatter(BaseFormatter):

136

"""

137

Pyupgrade code formatter plugin interface.

138

139

Upgrades Python syntax for newer language versions. Does not preserve AST

140

due to syntax transformations.

141

"""

142

name = "pyupgrade"

143

preserves_ast = False

144

config_section = "tool.pyupgrade" # Custom section

145

146

class NoneFormatter(BaseFormatter):

147

"""

148

Dummy formatter that returns code unmodified.

149

150

Useful for testing or when only pre-processors (isort, flynt) are desired.

151

"""

152

name = "dummy reformat"

153

preserves_ast = True

154

config_section = "tool.none"

155

```

156

157

### Configuration Types

158

159

Type definitions for formatter configuration.

160

161

```python { .api }

162

class FormatterConfig(TypedDict, total=False):

163

"""Base class for formatter configuration."""

164

line_length: int

165

target_version: str

166

167

class BlackCompatibleConfig(FormatterConfig, total=False):

168

"""Configuration for Black-compatible formatters."""

169

skip_string_normalization: bool

170

skip_magic_trailing_comma: bool

171

preview: bool

172

173

class BlackModeAttributes(TypedDict, total=False):

174

"""Type definition for Black Mode configuration items."""

175

target_versions: Set[TargetVersion]

176

line_length: int

177

string_normalization: bool

178

magic_trailing_comma: bool

179

preview: bool

180

```

181

182

### Configuration Utilities

183

184

Helper functions for reading and validating formatter configuration.

185

186

```python { .api }

187

def validate_target_versions(target_versions: List[str]) -> Set[TargetVersion]:

188

"""

189

Validate target-version configuration option.

190

191

Parameters:

192

- target_versions: List of target version strings

193

194

Returns:

195

Set of validated TargetVersion enum values

196

"""

197

198

def read_black_compatible_cli_args(

199

config: BlackCompatibleConfig,

200

line_length: int,

201

) -> List[str]:

202

"""

203

Convert Black-compatible configuration to CLI arguments.

204

205

Parameters:

206

- config: Black-compatible configuration dictionary

207

- line_length: Line length setting

208

209

Returns:

210

List of CLI arguments for the formatter

211

"""

212

```

213

214

## Usage Examples

215

216

### Basic Formatter Usage

217

218

```python

219

from darker.formatters import create_formatter, get_formatter_names

220

from darkgraylib.utils import TextDocument

221

222

# List available formatters

223

formatters = get_formatter_names()

224

print(f"Available formatters: {formatters}")

225

226

# Create a Black formatter

227

black_formatter = create_formatter("black")

228

print(f"Formatter: {black_formatter.name}")

229

print(f"Preserves AST: {black_formatter.preserves_ast}")

230

231

# Read configuration

232

config = black_formatter.read_config(

233

src=("src/",),

234

config="pyproject.toml"

235

)

236

237

# Format code

238

source_code = TextDocument.from_lines([

239

"def hello( name ):",

240

" print( f'Hello {name}!' )",

241

""

242

])

243

244

formatted_code = black_formatter.run(source_code, config)

245

print(formatted_code.string)

246

```

247

248

### Custom Formatter Plugin

249

250

```python

251

from darker.formatters.base_formatter import BaseFormatter

252

from darkgraylib.utils import TextDocument

253

from typing import Any

254

255

class MyFormatter(BaseFormatter):

256

"""Custom formatter example."""

257

258

name = "my-formatter"

259

preserves_ast = True

260

config_section = "tool.my-formatter"

261

262

def read_config(self, src: tuple[str, ...], config: str) -> dict:

263

"""Read custom configuration."""

264

# Load configuration from file or use defaults

265

return {

266

'line_length': 88,

267

'indent_size': 4,

268

}

269

270

def run(self, content: TextDocument, config: dict) -> TextDocument:

271

"""Apply custom formatting."""

272

# Implement custom formatting logic

273

lines = content.lines

274

formatted_lines = []

275

276

for line in lines:

277

# Example: ensure proper indentation

278

stripped = line.lstrip()

279

if stripped:

280

indent_level = (len(line) - len(stripped)) // config['indent_size']

281

formatted_line = ' ' * (indent_level * config['indent_size']) + stripped

282

else:

283

formatted_line = ''

284

formatted_lines.append(formatted_line)

285

286

return TextDocument.from_lines(formatted_lines)

287

288

# Register the formatter (would typically be done via entry points)

289

# This is just for illustration

290

formatter = MyFormatter()

291

```

292

293

### Formatter Configuration

294

295

```python

296

from darker.formatters import create_formatter

297

from darker.formatters.formatter_config import BlackCompatibleConfig

298

299

# Create formatter with specific configuration

300

formatter = create_formatter("black")

301

302

# Custom configuration

303

custom_config: BlackCompatibleConfig = {

304

'line_length': 100,

305

'skip_string_normalization': True,

306

'skip_magic_trailing_comma': False,

307

'target_version': 'py39',

308

'preview': False,

309

}

310

311

# Read configuration from file

312

config = formatter.read_config(

313

src=("src/", "tests/"),

314

config="pyproject.toml"

315

)

316

317

# Check configuration properties

318

line_length = formatter.get_line_length(config)

319

exclude_pattern = formatter.get_exclude(config)

320

print(f"Line length: {line_length}")

321

print(f"Exclude pattern: {exclude_pattern.pattern}")

322

```

323

324

### Entry Point Registration

325

326

To register a custom formatter plugin, add to your package's setup.py or pyproject.toml:

327

328

```toml

329

[project.entry-points."darker.formatter"]

330

my-formatter = "mypackage.formatters:MyFormatter"

331

```

332

333

```python

334

# setup.py equivalent

335

entry_points={

336

"darker.formatter": [

337

"my-formatter = mypackage.formatters:MyFormatter",

338

],

339

}

340

```

341

342

### Working with Formatter Results

343

344

```python

345

from darker.formatters import create_formatter

346

from darkgraylib.utils import TextDocument

347

348

formatter = create_formatter("black")

349

config = formatter.read_config(("src/",), "pyproject.toml")

350

351

original = TextDocument.from_lines([

352

"def poorly_formatted(x,y):",

353

" return x+y"

354

])

355

356

formatted = formatter.run(original, config)

357

358

if original != formatted:

359

print("Code was reformatted:")

360

print(f"Original:\n{original.string}")

361

print(f"Formatted:\n{formatted.string}")

362

else:

363

print("No formatting changes needed")

364

```