or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdconfiguration.mdformatting.mdindex.mdjupyter.mdserver.mdtypes.md

cli.mddocs/

0

# Command Line Interface

1

2

Complete CLI implementation providing file discovery, configuration loading, batch processing, and various output modes for formatting Python code from the command line.

3

4

## Capabilities

5

6

### Main CLI Entry Point

7

8

The primary command-line interface function with comprehensive option parsing and file processing.

9

10

```python { .api }

11

def main(

12

ctx: click.Context,

13

code: Optional[str] = None,

14

line_length: int = DEFAULT_LINE_LENGTH,

15

target_version: list[TargetVersion] = [],

16

pyi: bool = False,

17

ipynb: bool = False,

18

python_cell_magics: Sequence[str] = [],

19

skip_source_first_line: bool = False,

20

skip_string_normalization: bool = False,

21

skip_magic_trailing_comma: bool = False,

22

preview: bool = False,

23

unstable: bool = False,

24

enable_unstable_feature: list[Preview] = [],

25

check: bool = False,

26

diff: bool = False,

27

color: bool = False,

28

line_ranges: Sequence[str] = [],

29

fast: bool = False,

30

required_version: Optional[str] = None,

31

exclude: Optional[Pattern[str]] = None,

32

extend_exclude: Optional[Pattern[str]] = None,

33

force_exclude: Optional[Pattern[str]] = None,

34

stdin_filename: Optional[str] = None,

35

include: Pattern[str] = DEFAULT_INCLUDES,

36

workers: Optional[int] = None,

37

quiet: bool = False,

38

verbose: bool = False,

39

src: tuple[str, ...] = (),

40

config: Optional[str] = None

41

) -> None:

42

"""

43

Main CLI entry point with comprehensive option parsing.

44

45

Parameters:

46

- ctx: Click context for CLI handling

47

- code: Optional code string to format (instead of files)

48

- line_length: Maximum line length (default 88)

49

- target_version: List of Python versions to target

50

- pyi: Format as .pyi stub file

51

- ipynb: Format as Jupyter notebook

52

- python_cell_magics: IPython cell magics to recognize as Python

53

- skip_source_first_line: Skip shebang lines

54

- skip_string_normalization: Don't normalize string quotes

55

- skip_magic_trailing_comma: Don't use trailing comma splitting

56

- preview: Enable preview features

57

- unstable: Enable unstable features

58

- enable_unstable_feature: List of specific preview features to enable

59

- check: Exit with error if reformatting needed

60

- diff: Output unified diff instead of rewriting

61

- color: Colorize diff output

62

- line_ranges: Line ranges to format (e.g., "1-10,20-30")

63

- fast: Skip AST safety checks

64

- required_version: Require specific Black version

65

- exclude: File pattern to exclude (regex)

66

- extend_exclude: Additional exclude pattern

67

- force_exclude: Force exclude pattern (overrides include)

68

- stdin_filename: Filename for stdin input

69

- include: File pattern to include (default: Python files)

70

- workers: Number of parallel workers

71

- quiet: Minimal output

72

- verbose: Detailed output

73

- src: Source files/directories to format

74

- config: Configuration file path

75

76

Note:

77

Decorated with comprehensive Click options for all CLI functionality

78

"""

79

```

80

81

### File Discovery

82

83

Functions for discovering and filtering source files to format.

84

85

```python { .api }

86

def get_sources(

87

*,

88

root: Path,

89

src: tuple[str, ...],

90

quiet: bool,

91

verbose: bool,

92

include: Pattern[str],

93

exclude: Optional[Pattern[str]],

94

extend_exclude: Optional[Pattern[str]],

95

force_exclude: Optional[Pattern[str]],

96

report: Report,

97

stdin_filename: Optional[str]

98

) -> set[Path]:

99

"""

100

Compute the set of files to format.

101

102

Parameters:

103

- root: Project root directory

104

- src: Source file/directory paths

105

- quiet: Suppress output messages

106

- verbose: Enable detailed output

107

- include: File pattern to include

108

- exclude: Base exclude pattern

109

- extend_exclude: Additional exclude pattern

110

- force_exclude: Force exclude (overrides include)

111

- report: Report object for tracking results

112

- stdin_filename: Filename when reading from stdin

113

114

Returns:

115

Set of Path objects to format

116

117

Note:

118

- Automatically discovers .py, .pyi, and .ipynb files

119

- Respects .gitignore patterns

120

- Handles recursive directory traversal

121

"""

122

```

123

124

### Configuration Loading

125

126

Functions for loading and parsing Black configuration from pyproject.toml files.

127

128

```python { .api }

129

def read_pyproject_toml(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Optional[str]:

130

"""

131

Parse Black configuration from pyproject.toml files.

132

133

Parameters:

134

- ctx: Click context

135

- param: Click parameter being processed

136

- value: Path to configuration file (optional)

137

138

Returns:

139

Path to found configuration file, or None

140

141

Note:

142

- Searches for pyproject.toml in project hierarchy

143

- Loads [tool.black] section configuration

144

- Merges with command-line arguments

145

"""

146

147

def validate_regex(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Optional[Pattern[str]]:

148

"""

149

Validate and compile regex patterns for file filtering.

150

151

Parameters:

152

- ctx: Click context

153

- param: Click parameter being validated

154

- value: Regex pattern string

155

156

Returns:

157

Compiled regex Pattern object, or None

158

159

Raises:

160

- click.BadParameter: If regex is invalid

161

"""

162

```

163

164

## File Processing Functions

165

166

### Project Discovery

167

168

Utilities from the `black.files` module for project structure analysis.

169

170

```python { .api }

171

def find_project_root(srcs: tuple[str, ...], stdin_filename: Optional[str] = None) -> tuple[Path, str]:

172

"""

173

Find the project root directory and reason.

174

175

Parameters:

176

- srcs: Source file/directory paths

177

- stdin_filename: Filename for stdin processing

178

179

Returns:

180

Tuple of (project_root_path, reason_string)

181

"""

182

183

def find_pyproject_toml(path_search_start: tuple[str, ...], stdin_filename: Optional[str] = None) -> Optional[str]:

184

"""

185

Find pyproject.toml configuration file.

186

187

Parameters:

188

- path_search_start: Paths to start search from

189

- stdin_filename: Filename for stdin processing

190

191

Returns:

192

Path to pyproject.toml file, or None if not found

193

"""

194

195

def find_user_pyproject_toml() -> Path:

196

"""

197

Find user-level pyproject.toml configuration.

198

199

Returns:

200

Path to user configuration file

201

"""

202

203

def parse_pyproject_toml(path_config: str) -> dict[str, Any]:

204

"""

205

Parse Black configuration from pyproject.toml file.

206

207

Parameters:

208

- path_config: Path to configuration file

209

210

Returns:

211

Dictionary of configuration values

212

213

Raises:

214

- OSError: If file cannot be read

215

- tomllib.TOMLDecodeError: If TOML is invalid

216

"""

217

```

218

219

### File Generation and Filtering

220

221

```python { .api }

222

def gen_python_files(

223

paths: Iterable[Path],

224

root: Path,

225

include: Pattern[str],

226

exclude: Pattern[str],

227

extend_exclude: Optional[Pattern[str]],

228

force_exclude: Optional[Pattern[str]],

229

report: Report,

230

gitignore: Optional[PathSpec] = None

231

) -> Iterator[Path]:

232

"""

233

Generate Python files from given paths with filtering.

234

235

Parameters:

236

- paths: Iterable of source paths

237

- root: Project root directory

238

- include: Include pattern

239

- exclude: Exclude pattern

240

- extend_exclude: Additional exclude pattern

241

- force_exclude: Force exclude pattern

242

- report: Report object for tracking

243

- gitignore: Optional gitignore patterns

244

245

Yields:

246

Path objects for files to process

247

"""

248

249

def get_gitignore(root: Path) -> PathSpec:

250

"""

251

Get gitignore patterns for the project.

252

253

Parameters:

254

- root: Project root directory

255

256

Returns:

257

PathSpec object with gitignore patterns

258

"""

259

260

def path_is_excluded(

261

normalized_path: str,

262

pattern: Optional[Pattern[str]]

263

) -> bool:

264

"""

265

Check if path matches exclusion pattern.

266

267

Parameters:

268

- normalized_path: Normalized file path

269

- pattern: Regex pattern to match against

270

271

Returns:

272

True if path should be excluded

273

"""

274

```

275

276

## Line Range Processing

277

278

Functions for parsing and handling line range specifications.

279

280

```python { .api }

281

def parse_line_ranges(line_ranges: Sequence[str]) -> list[tuple[int, int]]:

282

"""

283

Parse line range specifications from CLI arguments.

284

285

Parameters:

286

- line_ranges: List of line range strings (e.g., ["1-10", "20-30"])

287

288

Returns:

289

List of (start_line, end_line) tuples

290

291

Note:

292

- Supports formats: "N", "N-M", "N-", "-M"

293

- Line numbers are 1-indexed

294

- Overlapping ranges are merged

295

296

Raises:

297

- ValueError: If range format is invalid

298

"""

299

300

def sanitized_lines(

301

src_contents: str,

302

lines: Collection[tuple[int, int]]

303

) -> Collection[tuple[int, int]]:

304

"""

305

Sanitize line ranges against actual file content.

306

307

Parameters:

308

- src_contents: Source file content

309

- lines: Line ranges to sanitize

310

311

Returns:

312

Sanitized line ranges within file bounds

313

"""

314

315

def adjusted_lines(

316

original_lines: Collection[tuple[int, int]],

317

src_contents: str,

318

dst_contents: str

319

) -> Collection[tuple[int, int]]:

320

"""

321

Adjust line ranges after formatting changes.

322

323

Parameters:

324

- original_lines: Original line ranges

325

- src_contents: Original file content

326

- dst_contents: Formatted file content

327

328

Returns:

329

Adjusted line ranges accounting for formatting changes

330

"""

331

```

332

333

## Usage Examples

334

335

### Basic CLI Usage

336

337

```python

338

import subprocess

339

import sys

340

341

# Format single file

342

result = subprocess.run([

343

sys.executable, "-m", "black", "script.py"

344

], capture_output=True, text=True)

345

346

# Format with options

347

result = subprocess.run([

348

sys.executable, "-m", "black",

349

"--line-length", "79",

350

"--target-version", "py39",

351

"--diff",

352

"src/"

353

], capture_output=True, text=True)

354

355

print(result.stdout)

356

```

357

358

### Programmatic CLI Access

359

360

```python

361

import click

362

import black

363

364

# Create Click context and call main

365

ctx = click.Context(black.main)

366

try:

367

black.main.invoke(ctx,

368

src=('src/',),

369

line_length=79,

370

target_version=[black.TargetVersion.PY39],

371

diff=True,

372

check=False

373

)

374

except SystemExit as e:

375

print(f"Exit code: {e.code}")

376

```

377

378

### File Discovery

379

380

```python

381

from pathlib import Path

382

import re

383

384

# Setup for file discovery

385

root = Path(".")

386

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

387

include = re.compile(r"\.pyi?$")

388

exclude = re.compile(r"/(\.git|__pycache__|build)/")

389

390

report = black.Report()

391

392

# Find files to format

393

sources = black.get_sources(

394

root=root,

395

src=src_paths,

396

quiet=False,

397

verbose=True,

398

include=include,

399

exclude=exclude,

400

extend_exclude=None,

401

force_exclude=None,

402

report=report,

403

stdin_filename=None

404

)

405

406

print(f"Found {len(sources)} files to format:")

407

for source in sorted(sources):

408

print(f" {source}")

409

```

410

411

### Configuration Loading

412

413

```python

414

# Find and load configuration

415

config_path = black.find_pyproject_toml((".",), stdin_filename=None)

416

if config_path:

417

config = black.parse_pyproject_toml(config_path)

418

print(f"Loaded config from {config_path}:")

419

print(config)

420

421

# Apply config to mode

422

mode_kwargs = {}

423

if "line_length" in config:

424

mode_kwargs["line_length"] = config["line_length"]

425

if "target_version" in config:

426

target_versions = {

427

black.TargetVersion[v.upper().replace(".", "")]

428

for v in config["target_version"]

429

}

430

mode_kwargs["target_versions"] = target_versions

431

432

mode = black.Mode(**mode_kwargs)

433

```

434

435

### Line Range Formatting

436

437

```python

438

# Parse line ranges from CLI-style strings

439

range_specs = ["1-10", "20-25", "50-"]

440

line_ranges = black.parse_line_ranges(range_specs)

441

print(f"Parsed ranges: {line_ranges}")

442

443

# Format specific lines only

444

code = open("example.py").read()

445

formatted = black.format_str(

446

code,

447

mode=black.Mode(),

448

lines=line_ranges

449

)

450

```

451

452

### Batch Processing

453

454

```python

455

from concurrent.futures import ThreadPoolExecutor

456

from pathlib import Path

457

458

def format_file(file_path: Path) -> bool:

459

"""Format a single file and return if changed."""

460

try:

461

return black.format_file_in_place(

462

file_path,

463

fast=False,

464

mode=black.Mode(),

465

write_back=black.WriteBack.YES

466

)

467

except Exception as e:

468

print(f"Error formatting {file_path}: {e}")

469

return False

470

471

# Process files in parallel

472

source_files = list(Path("src").glob("**/*.py"))

473

with ThreadPoolExecutor(max_workers=4) as executor:

474

results = list(executor.map(format_file, source_files))

475

476

changed_count = sum(results)

477

print(f"Formatted {changed_count} files")

478

```

479

480

### Custom Output Handling

481

482

```python

483

import io

484

import contextlib

485

486

# Capture CLI output

487

output_buffer = io.StringIO()

488

with contextlib.redirect_stdout(output_buffer):

489

try:

490

ctx = click.Context(black.main)

491

black.main.invoke(ctx,

492

src=('example.py',),

493

diff=True,

494

color=False

495

)

496

except SystemExit:

497

pass

498

499

diff_output = output_buffer.getvalue()

500

print("Captured diff:")

501

print(diff_output)

502

```

503

504

## Types

505

506

```python { .api }

507

# Path processing types

508

PathLike = Union[str, Path]

509

PathSpec = pathspec.PathSpec

510

Pattern = re.Pattern[str]

511

512

# File processing

513

FileList = set[Path]

514

SourcePaths = tuple[str, ...]

515

516

# Configuration

517

ConfigDict = dict[str, Any]

518

LineRange = tuple[int, int]

519

LineRanges = Collection[LineRange]

520

```