or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

command-definition.mdcore-classes.mdexception-handling.mdindex.mdparameter-types.mdterminal-ui.mdutilities.md

utilities.mddocs/

0

# Utilities and Helper Functions

1

2

Utility functions for file handling, stream management, text formatting, and other common CLI application needs. Click provides a comprehensive set of helper functions that simplify common command-line application tasks.

3

4

## Capabilities

5

6

### File and Stream Operations

7

8

Functions for handling files, streams, and I/O operations with enhanced features.

9

10

```python { .api }

11

def open_file(filename, mode="r", encoding=None, errors="strict", lazy=False, atomic=False):

12

"""

13

Open file with special handling for "-" (stdin/stdout).

14

15

Parameters:

16

- filename: File path or "-" for stdin/stdout

17

- mode: File open mode ('r', 'w', 'a', 'rb', 'wb', etc.)

18

- encoding: Text encoding (None for binary modes)

19

- errors: Error handling strategy ('strict', 'ignore', 'replace')

20

- lazy: Open file lazily on first access

21

- atomic: Write to temporary file then move (for write modes)

22

23

Returns:

24

File object or LazyFile/KeepOpenFile wrapper

25

"""

26

27

def get_binary_stream(name):

28

"""

29

Get system binary stream.

30

31

Parameters:

32

- name: Stream name ("stdin", "stdout", "stderr")

33

34

Returns:

35

Binary stream object

36

"""

37

38

def get_text_stream(name, encoding=None, errors="strict"):

39

"""

40

Get system text stream.

41

42

Parameters:

43

- name: Stream name ("stdin", "stdout", "stderr")

44

- encoding: Text encoding (None for system default)

45

- errors: Error handling strategy

46

47

Returns:

48

Text stream object

49

"""

50

```

51

52

**Usage Examples:**

53

54

```python

55

@click.command()

56

@click.option('--input', default='-', help='Input file (- for stdin)')

57

@click.option('--output', default='-', help='Output file (- for stdout)')

58

def process_streams(input, output):

59

"""Process data from input to output."""

60

with click.open_file(input, 'r') as infile:

61

with click.open_file(output, 'w') as outfile:

62

for line in infile:

63

processed = line.upper()

64

outfile.write(processed)

65

66

@click.command()

67

@click.option('--output', type=click.Path())

68

def atomic_write(output):

69

"""Write file atomically."""

70

with click.open_file(output, 'w', atomic=True) as f:

71

f.write('This will be written atomically\n')

72

f.write('Either all content is written or none\n')

73

click.echo(f'File written atomically to {output}')

74

75

@click.command()

76

def stream_examples():

77

"""Demonstrate stream operations."""

78

# Get system streams

79

stdout = click.get_text_stream('stdout')

80

stderr = click.get_text_stream('stderr')

81

82

stdout.write('This goes to stdout\n')

83

stderr.write('This goes to stderr\n')

84

85

# Binary streams

86

binary_stdout = click.get_binary_stream('stdout')

87

binary_stdout.write(b'Binary data to stdout\n')

88

```

89

90

### Directory and Path Utilities

91

92

Functions for handling application directories and path operations.

93

94

```python { .api }

95

def get_app_dir(app_name, roaming=True, force_posix=False):

96

"""

97

Get application configuration directory.

98

99

Parameters:

100

- app_name: Name of the application

101

- roaming: Use roaming directory on Windows

102

- force_posix: Force POSIX-style paths even on Windows

103

104

Returns:

105

Path to application directory

106

107

Platform behavior:

108

- Windows: %APPDATA%\\app_name (roaming) or %LOCALAPPDATA%\\app_name

109

- macOS: ~/Library/Application Support/app_name

110

- Linux: ~/.config/app_name (or $XDG_CONFIG_HOME/app_name)

111

"""

112

113

def format_filename(filename, shorten=False):

114

"""

115

Format filename for display.

116

117

Parameters:

118

- filename: Filename to format

119

- shorten: Shorten very long filenames

120

121

Returns:

122

Formatted filename string

123

"""

124

```

125

126

**Usage Examples:**

127

128

```python

129

import os

130

131

@click.command()

132

@click.argument('app_name')

133

def show_app_dir(app_name):

134

"""Show application directory for given app name."""

135

app_dir = click.get_app_dir(app_name)

136

click.echo(f'App directory: {app_dir}')

137

138

# Create directory if it doesn't exist

139

os.makedirs(app_dir, exist_ok=True)

140

141

# Create a config file

142

config_file = os.path.join(app_dir, 'config.ini')

143

with open(config_file, 'w') as f:

144

f.write(f'[{app_name}]\n')

145

f.write('debug = false\n')

146

147

click.echo(f'Created config: {click.format_filename(config_file)}')

148

149

@click.command()

150

def config_example():

151

"""Example of using app directory for configuration."""

152

app_dir = click.get_app_dir('myapp')

153

config_path = os.path.join(app_dir, 'settings.json')

154

155

# Ensure directory exists

156

os.makedirs(app_dir, exist_ok=True)

157

158

if os.path.exists(config_path):

159

click.echo(f'Loading config from {click.format_filename(config_path)}')

160

else:

161

click.echo(f'Creating default config at {click.format_filename(config_path)}')

162

import json

163

default_config = {

164

'debug': False,

165

'log_level': 'INFO',

166

'max_retries': 3

167

}

168

with open(config_path, 'w') as f:

169

json.dump(default_config, f, indent=2)

170

```

171

172

### Context Management

173

174

Functions for accessing and managing Click contexts.

175

176

```python { .api }

177

def get_current_context(silent=False):

178

"""

179

Get current Click context from thread-local storage.

180

181

Parameters:

182

- silent: Return None instead of raising error if no context

183

184

Returns:

185

Current Context object or None

186

187

Raises:

188

RuntimeError: If no context is available and silent=False

189

"""

190

```

191

192

**Usage Examples:**

193

194

```python

195

def helper_function():

196

"""Helper function that needs access to current context."""

197

ctx = click.get_current_context(silent=True)

198

if ctx:

199

return ctx.obj.get('debug', False)

200

return False

201

202

@click.group()

203

@click.option('--debug', is_flag=True)

204

@click.pass_context

205

def main_cli(ctx, debug):

206

"""Main CLI with shared context."""

207

ctx.ensure_object(dict)

208

ctx.obj['debug'] = debug

209

210

@main_cli.command()

211

def subcommand():

212

"""Subcommand that uses helper function."""

213

debug_mode = helper_function()

214

click.echo(f'Debug mode: {debug_mode}')

215

216

# Context-aware logging

217

def log_message(message, level='INFO'):

218

"""Log message with context-aware formatting."""

219

ctx = click.get_current_context(silent=True)

220

prefix = ''

221

222

if ctx and ctx.obj and ctx.obj.get('verbose'):

223

prefix = f'[{level}] {ctx.info_name}: '

224

225

click.echo(f'{prefix}{message}')

226

227

@main_cli.command()

228

@click.option('--verbose', is_flag=True)

229

@click.pass_context

230

def logging_example(ctx, verbose):

231

"""Example of context-aware logging."""

232

ctx.obj['verbose'] = verbose

233

234

log_message('Starting operation')

235

log_message('Processing data', 'DEBUG')

236

log_message('Operation complete')

237

```

238

239

### Text and String Utilities

240

241

Helper functions for text processing and string manipulation.

242

243

```python { .api }

244

# Internal utility functions (not directly exported but used by Click)

245

def make_str(value):

246

"""Convert value to valid string representation."""

247

248

def make_default_short_help(help, max_length=45):

249

"""Create condensed help string from longer help text."""

250

251

def safecall(func):

252

"""Wrap function to swallow exceptions silently."""

253

```

254

255

**Usage Examples:**

256

257

```python

258

# Custom help formatting

259

def create_short_help(long_help, max_len=40):

260

"""Create short help from long help text."""

261

if not long_help:

262

return None

263

264

# Simple truncation with ellipsis

265

if len(long_help) <= max_len:

266

return long_help

267

268

# Find last space before max length

269

truncated = long_help[:max_len]

270

last_space = truncated.rfind(' ')

271

272

if last_space > max_len * 0.7: # If space is reasonably close

273

return truncated[:last_space] + '...'

274

else:

275

return truncated + '...'

276

277

@click.group()

278

def cli_with_short_help():

279

"""Main CLI group."""

280

pass

281

282

@cli_with_short_help.command()

283

def long_command():

284

"""This is a very long help text that describes in great detail what this command does and provides comprehensive information about its purpose and usage patterns."""

285

click.echo('Command executed')

286

287

# Set short help manually

288

long_command.short_help = create_short_help(long_command.help)

289

```

290

291

### File Wrapper Classes

292

293

Special file classes for advanced file handling scenarios.

294

295

```python { .api }

296

class LazyFile:

297

"""

298

File wrapper that opens the file lazily on first access.

299

Used internally by click.open_file() with lazy=True.

300

"""

301

302

class KeepOpenFile:

303

"""

304

File wrapper that prevents closing when used as context manager.

305

Useful for stdin/stdout that shouldn't be closed.

306

"""

307

308

class PacifyFlushWrapper:

309

"""

310

Wrapper that suppresses BrokenPipeError on flush().

311

Handles cases where output pipe is closed by receiver.

312

"""

313

```

314

315

**Usage Examples:**

316

317

```python

318

# Custom file handling with error recovery

319

@click.command()

320

@click.option('--input', type=click.File('r'))

321

@click.option('--output', type=click.File('w'))

322

def robust_copy(input, output):

323

"""Copy with error handling for broken pipes."""

324

try:

325

for line in input:

326

output.write(line.upper())

327

output.flush() # May raise BrokenPipeError

328

except BrokenPipeError:

329

# Handle gracefully - receiver closed pipe

330

click.echo('Output pipe closed by receiver', err=True)

331

except KeyboardInterrupt:

332

click.echo('\nOperation cancelled', err=True)

333

raise click.Abort()

334

335

# Working with lazy files

336

@click.command()

337

@click.option('--config', type=click.Path())

338

def lazy_config(config):

339

"""Example of lazy file loading."""

340

if config:

341

# File won't be opened until first access

342

with click.open_file(config, 'r', lazy=True) as f:

343

if some_condition():

344

# File is opened here on first read

345

content = f.read()

346

click.echo(f'Config loaded: {len(content)} bytes')

347

# If condition was false, file never gets opened

348

349

# Atomic file operations

350

@click.command()

351

@click.argument('output_file')

352

def atomic_json_write(output_file):

353

"""Write JSON atomically to prevent corruption."""

354

import json

355

import time

356

357

data = {

358

'timestamp': time.time(),

359

'status': 'processing',

360

'items': list(range(1000))

361

}

362

363

# Atomic write - either succeeds completely or fails completely

364

with click.open_file(output_file, 'w', atomic=True) as f:

365

json.dump(data, f, indent=2)

366

# If any error occurs here, original file is unchanged

367

368

click.echo(f'Data written atomically to {output_file}')

369

```

370

371

### Text Formatting and Help Generation

372

373

Functions and classes for formatting help text and wrapping text content.

374

375

```python { .api }

376

class HelpFormatter:

377

def __init__(self, indent_increment=2, width=None, max_width=None):

378

"""

379

Format text-based help pages for commands.

380

381

Parameters:

382

- indent_increment: Additional increment for each indentation level

383

- width: Text width (defaults to terminal width, max 78)

384

- max_width: Maximum width allowed

385

"""

386

387

def write(self, string):

388

"""Write a string into the internal buffer."""

389

390

def indent(self):

391

"""Increase the indentation level."""

392

393

def dedent(self):

394

"""Decrease the indentation level."""

395

396

def write_usage(self, prog, args="", prefix=None):

397

"""

398

Write a usage line into the buffer.

399

400

Parameters:

401

- prog: Program name

402

- args: Whitespace separated list of arguments

403

- prefix: Prefix for the first line (defaults to "Usage: ")

404

"""

405

406

def write_heading(self, heading):

407

"""Write a heading into the buffer."""

408

409

def write_paragraph(self):

410

"""Write a paragraph break into the buffer."""

411

412

def write_text(self, text):

413

"""Write re-indented text with paragraph preservation."""

414

415

def write_dl(self, rows, col_max=30, col_spacing=2):

416

"""

417

Write a definition list (used for options and commands).

418

419

Parameters:

420

- rows: List of (term, description) tuples

421

- col_max: Maximum width of first column

422

- col_spacing: Spaces between columns

423

"""

424

425

def section(self, name):

426

"""Context manager that writes heading and indents."""

427

428

def indentation(self):

429

"""Context manager that increases indentation."""

430

431

def getvalue(self):

432

"""Return the buffer contents as string."""

433

434

def wrap_text(text, width=78, initial_indent="", subsequent_indent="", preserve_paragraphs=False):

435

"""

436

Intelligent text wrapping with paragraph handling.

437

438

Parameters:

439

- text: Text to wrap

440

- width: Maximum line width

441

- initial_indent: Indent for first line

442

- subsequent_indent: Indent for continuation lines

443

- preserve_paragraphs: Handle paragraphs intelligently (separated by empty lines)

444

445

Returns:

446

Wrapped text string

447

448

Notes:

449

- When preserve_paragraphs=True, paragraphs are defined by two empty lines

450

- Lines starting with \\b character are not rewrapped

451

- Handles mixed indentation levels within paragraphs

452

"""

453

```

454

455

**Usage Examples:**

456

457

```python

458

@click.command()

459

def formatting_examples():

460

"""Demonstrate text formatting utilities."""

461

# Create a help formatter

462

formatter = click.HelpFormatter(indent_increment=4, width=60)

463

464

# Write structured help content

465

formatter.write_heading('Options')

466

formatter.indent()

467

formatter.write_dl([

468

('--verbose, -v', 'Enable verbose output'),

469

('--config FILE', 'Configuration file path'),

470

('--help', 'Show this message and exit')

471

])

472

formatter.dedent()

473

474

# Get formatted result

475

help_text = formatter.getvalue()

476

click.echo(help_text)

477

478

# Text wrapping examples

479

long_text = "This is a very long line of text that needs to be wrapped to fit within a reasonable line length for display in terminal applications."

480

481

wrapped = click.wrap_text(long_text, width=40)

482

click.echo("Basic wrapping:")

483

click.echo(wrapped)

484

485

# With indentation

486

indented = click.wrap_text(long_text, width=40,

487

initial_indent=" * ",

488

subsequent_indent=" ")

489

click.echo("\nWith indentation:")

490

click.echo(indented)

491

492

# Paragraph preservation

493

paragraphs = """First paragraph with some text.

494

495

Second paragraph after empty line.

496

497

Indented paragraph that should

498

maintain its indentation level."""

499

500

preserved = click.wrap_text(paragraphs, width=30, preserve_paragraphs=True)

501

click.echo("\nParagraph preservation:")

502

click.echo(preserved)

503

504

# Custom help formatter

505

class CustomHelpFormatter(click.HelpFormatter):

506

"""Custom help formatter with different styling."""

507

508

def write_heading(self, heading):

509

"""Write heading with custom styling."""

510

self.write(f"{'':>{self.current_indent}}=== {heading.upper()} ===\n")

511

512

@click.command()

513

def custom_help_example():

514

"""Command with custom help formatting."""

515

pass

516

517

# Override the formatter class

518

custom_help_example.context_settings = {'formatter_class': CustomHelpFormatter}

519

```

520

521

### Integration Utilities

522

523

Functions for integrating Click with other systems and frameworks.

524

525

```python

526

# Environment integration

527

@click.command()

528

@click.option('--config-dir',

529

envvar='MY_APP_CONFIG_DIR',

530

default=lambda: click.get_app_dir('myapp'),

531

help='Configuration directory')

532

def env_integration(config_dir):

533

"""Example of environment variable integration."""

534

click.echo(f'Using config directory: {config_dir}')

535

536

# Shell integration

537

@click.command()

538

@click.option('--shell-complete', is_flag=True, hidden=True)

539

def shell_integration(shell_complete):

540

"""Command with shell completion support."""

541

if shell_complete:

542

# This would be handled by Click's completion system

543

return

544

545

click.echo('Command executed')

546

547

# Testing utilities integration

548

def create_test_context(command, args=None):

549

"""Create context for testing Click commands."""

550

if args is None:

551

args = []

552

553

return command.make_context('test', args)

554

555

# Example test helper

556

def test_command_helper():

557

"""Helper for testing Click commands."""

558

@click.command()

559

@click.argument('name')

560

@click.option('--greeting', default='Hello')

561

def greet(name, greeting):

562

click.echo(f'{greeting}, {name}!')

563

564

# Create test context

565

ctx = create_test_context(greet, ['World', '--greeting', 'Hi'])

566

567

# This would be used in actual testing

568

return ctx

569

```