or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdimport-autotag.mdindex.mdlibrary-management.mdplugin-system.mdquery-system.mduser-interface.mdutilities-templates.md

utilities-templates.mddocs/

0

# Utilities and Templates

1

2

File operations, path manipulation, template formatting, and various utility functions for safe file handling and flexible path generation. These utilities provide the foundation for beets' file management and path formatting capabilities.

3

4

## Capabilities

5

6

### File Operations

7

8

Safe file operations with cross-platform compatibility and error handling.

9

10

```python { .api }

11

def normpath(path: Union[str, bytes]) -> bytes:

12

"""

13

Normalize a filesystem path to bytes representation.

14

15

Parameters:

16

- path: Filesystem path as string or bytes

17

18

Returns:

19

Normalized path as bytes

20

"""

21

22

def syspath(path: Union[str, bytes]) -> str:

23

"""

24

Convert path to system's preferred string representation.

25

26

Parameters:

27

- path: Filesystem path as string or bytes

28

29

Returns:

30

Path as Unicode string suitable for system calls

31

"""

32

33

def bytestring_path(path: Union[str, bytes]) -> bytes:

34

"""

35

Convert path to bytestring representation.

36

37

Parameters:

38

- path: Filesystem path as string or bytes

39

40

Returns:

41

Path as bytes using filesystem encoding

42

"""

43

44

def displayable_path(path: Union[str, bytes]) -> str:

45

"""

46

Convert path to Unicode string safe for display.

47

48

Parameters:

49

- path: Filesystem path as string or bytes

50

51

Returns:

52

Unicode string representation of path

53

"""

54

55

def samefile(p1: Union[str, bytes], p2: Union[str, bytes]) -> bool:

56

"""

57

Check if two paths refer to the same file.

58

59

Parameters:

60

- p1: First path to compare

61

- p2: Second path to compare

62

63

Returns:

64

True if paths refer to the same file

65

"""

66

```

67

68

### Safe File Operations

69

70

Functions for moving, copying, and linking files with error handling.

71

72

```python { .api }

73

def move(src: Union[str, bytes], dest: Union[str, bytes]) -> None:

74

"""

75

Safely move a file from source to destination.

76

77

Parameters:

78

- src: Source file path

79

- dest: Destination file path

80

81

Raises:

82

FileOperationError: If move operation fails

83

"""

84

85

def copy(src: Union[str, bytes], dest: Union[str, bytes]) -> None:

86

"""

87

Safely copy a file from source to destination.

88

89

Parameters:

90

- src: Source file path

91

- dest: Destination file path

92

93

Raises:

94

FileOperationError: If copy operation fails

95

"""

96

97

def link(src: Union[str, bytes], dest: Union[str, bytes]) -> None:

98

"""

99

Create a hard link from source to destination.

100

101

Parameters:

102

- src: Source file path

103

- dest: Destination file path

104

105

Raises:

106

FileOperationError: If linking is not supported or fails

107

"""

108

109

def reflink(src: Union[str, bytes], dest: Union[str, bytes]) -> None:

110

"""

111

Create a copy-on-write link (reflink) if supported.

112

113

Parameters:

114

- src: Source file path

115

- dest: Destination file path

116

117

Raises:

118

FileOperationError: If reflinking is not supported or fails

119

"""

120

121

def unique_path(path: Union[str, bytes]) -> Union[str, bytes]:

122

"""

123

Generate a unique file path if the given path already exists.

124

125

Parameters:

126

- path: Desired file path

127

128

Returns:

129

Unique path (original or with numeric suffix)

130

"""

131

```

132

133

### Path Utilities

134

135

Functions for path manipulation and analysis.

136

137

```python { .api }

138

def ancestry(path: Union[str, bytes]) -> List[Union[str, bytes]]:

139

"""

140

Get all parent directories of a path.

141

142

Parameters:

143

- path: Filesystem path

144

145

Returns:

146

List of parent directories from deepest to root

147

"""

148

149

def components(path: Union[str, bytes]) -> List[Union[str, bytes]]:

150

"""

151

Split path into individual components.

152

153

Parameters:

154

- path: Filesystem path

155

156

Returns:

157

List of path components

158

"""

159

160

def as_string(value: Any) -> str:

161

"""

162

Convert value to Unicode string safely.

163

164

Parameters:

165

- value: Value to convert (any type)

166

167

Returns:

168

Unicode string representation

169

"""

170

```

171

172

### Template System

173

174

Flexible template formatting system for generating paths and strings.

175

176

```python { .api }

177

class Template:

178

"""Template for string formatting with conditional logic and functions."""

179

180

def __init__(self, template_string: str):

181

"""

182

Initialize template from format string.

183

184

Parameters:

185

- template_string: Template format string with $field and %function{} syntax

186

"""

187

188

def substitute(self, values: Dict[str, Any], functions: Dict[str, Callable] = None) -> str:

189

"""

190

Substitute values into template.

191

192

Parameters:

193

- values: Dictionary mapping field names to values

194

- functions: Optional dictionary of template functions

195

196

Returns:

197

Formatted string with substitutions applied

198

"""

199

200

def template(template_string: str) -> Template:

201

"""

202

Create Template object from format string.

203

204

Parameters:

205

- template_string: Template format string

206

207

Returns:

208

Template object ready for substitution

209

"""

210

```

211

212

### Template Environment

213

214

Context for template evaluation with built-in functions.

215

216

```python { .api }

217

class Environment:

218

"""Template evaluation environment with built-in functions."""

219

220

def __init__(self, values: Dict[str, Any]):

221

"""

222

Initialize environment with field values.

223

224

Parameters:

225

- values: Dictionary mapping field names to values

226

"""

227

228

def call_function(self, name: str, args: List[Any]) -> Any:

229

"""

230

Call a template function by name.

231

232

Parameters:

233

- name: Function name

234

- args: Function arguments

235

236

Returns:

237

Function result

238

"""

239

```

240

241

### Built-in Template Functions

242

243

Template functions available for path formatting and string manipulation.

244

245

```python { .api }

246

# String transformation functions

247

def tmpl_upper(text: str) -> str:

248

"""Convert text to uppercase."""

249

250

def tmpl_lower(text: str) -> str:

251

"""Convert text to lowercase."""

252

253

def tmpl_title(text: str) -> str:

254

"""Convert text to title case."""

255

256

# String manipulation functions

257

def tmpl_left(text: str, length: int) -> str:

258

"""Take leftmost characters up to length."""

259

260

def tmpl_right(text: str, length: int) -> str:

261

"""Take rightmost characters up to length."""

262

263

def tmpl_if(condition: Any, then_value: Any, else_value: Any = '') -> Any:

264

"""Conditional expression (if condition then then_value else else_value)."""

265

266

def tmpl_ifdef(field: str, then_value: Any, else_value: Any = '') -> Any:

267

"""Conditional based on field existence (if field defined then then_value else else_value)."""

268

269

def tmpl_asciify(text: str) -> str:

270

"""Convert Unicode text to ASCII approximation."""

271

272

def tmpl_sanitize(text: str) -> str:

273

"""Remove/replace characters invalid in filenames."""

274

```

275

276

## Template Syntax and Usage

277

278

### Basic Field Substitution

279

280

```python

281

from beets.util.functemplate import template

282

283

# Basic field substitution

284

tmpl = template('$artist - $title')

285

result = tmpl.substitute({

286

'artist': 'The Beatles',

287

'title': 'Hey Jude'

288

})

289

# Result: "The Beatles - Hey Jude"

290

291

# Path template

292

path_tmpl = template('$albumartist/$album/$track $title')

293

path = path_tmpl.substitute({

294

'albumartist': 'The Beatles',

295

'album': 'Abbey Road',

296

'track': 5,

297

'title': 'Here Comes the Sun'

298

})

299

# Result: "The Beatles/Abbey Road/05 Here Comes the Sun"

300

```

301

302

### Template Functions

303

304

```python

305

# String transformations

306

tmpl = template('%upper{$artist} - %lower{$title}')

307

result = tmpl.substitute({

308

'artist': 'The Beatles',

309

'title': 'HEY JUDE'

310

})

311

# Result: "THE BEATLES - hey jude"

312

313

# Conditional formatting

314

tmpl = template('$artist%if{$year, - $year}')

315

result = tmpl.substitute({

316

'artist': 'The Beatles',

317

'year': 1969

318

})

319

# Result: "The Beatles - 1969"

320

321

# String truncation

322

tmpl = template('%left{$title,20}')

323

result = tmpl.substitute({

324

'title': 'Very Long Song Title That Needs Truncation'

325

})

326

# Result: "Very Long Song Title "

327

```

328

329

### Advanced Template Features

330

331

```python

332

# Nested conditionals

333

tmpl = template('%if{$albumartist,$albumartist,%if{$artist,$artist,Unknown Artist}}')

334

335

# Field existence checks

336

tmpl = template('%ifdef{$year,$year,0000}')

337

338

# Multiple functions

339

tmpl = template('%upper{%left{$artist,10}}')

340

341

# Filename sanitization

342

tmpl = template('%sanitize{$artist - $title}')

343

```

344

345

### Path Format Configuration

346

347

```python

348

from beets import config

349

from beets.util.functemplate import template

350

351

# Get path formats from configuration

352

path_formats = config['paths'].get(dict)

353

354

# Common path formats

355

formats = {

356

'default': '$albumartist/$album/$track $title',

357

'singleton': 'Non-Album/$artist - $title',

358

'comp': 'Compilations/$album/$track $title',

359

'albumtype:soundtrack': 'Soundtracks/$album/$track $title'

360

}

361

362

# Apply formats

363

for pattern, format_str in formats.items():

364

tmpl = template(format_str)

365

# Use template for path generation

366

```

367

368

## File Operation Examples

369

370

### Safe File Moving

371

372

```python

373

from beets.util import move, copy, samefile

374

from beets.library import FileOperationError

375

376

def safe_file_operation(src_path, dest_path, operation='move'):

377

"""Safely perform file operations with error handling."""

378

379

try:

380

if samefile(src_path, dest_path):

381

print("Source and destination are the same file")

382

return

383

384

if operation == 'move':

385

move(src_path, dest_path)

386

print(f"Moved: {src_path} -> {dest_path}")

387

elif operation == 'copy':

388

copy(src_path, dest_path)

389

print(f"Copied: {src_path} -> {dest_path}")

390

391

except FileOperationError as e:

392

print(f"File operation failed: {e}")

393

raise

394

```

395

396

### Path Generation

397

398

```python

399

from beets.util import unique_path, displayable_path

400

from beets.util.functemplate import template

401

402

def generate_item_path(item, library):

403

"""Generate destination path for a library item."""

404

405

# Get path format template

406

path_template = template(library.path_format_for_item(item))

407

408

# Generate path

409

dest_path = path_template.substitute(item.formatted())

410

411

# Ensure path is unique

412

dest_path = unique_path(dest_path)

413

414

# Convert to displayable format

415

display_path = displayable_path(dest_path)

416

417

return dest_path, display_path

418

```

419

420

### Directory Creation

421

422

```python

423

import os

424

from beets.util import ancestry, displayable_path

425

426

def ensure_directory_exists(path):

427

"""Create directory and all parent directories if needed."""

428

429

directory = os.path.dirname(path)

430

431

if not os.path.exists(directory):

432

try:

433

os.makedirs(directory, exist_ok=True)

434

print(f"Created directory: {displayable_path(directory)}")

435

except OSError as e:

436

print(f"Failed to create directory {displayable_path(directory)}: {e}")

437

raise

438

```

439

440

## Art and Image Utilities

441

442

### Image Resizing

443

444

```python { .api }

445

class ArtResizer:

446

"""Utility for resizing and processing album artwork."""

447

448

def __init__(self, quality: int = 95):

449

"""

450

Initialize art resizer.

451

452

Parameters:

453

- quality: JPEG quality for output (1-100)

454

"""

455

456

def resize(self, image_path: str, output_path: str, size: Tuple[int, int]) -> None:

457

"""

458

Resize image to specified dimensions.

459

460

Parameters:

461

- image_path: Source image file path

462

- output_path: Destination image file path

463

- size: Target (width, height) tuple

464

"""

465

466

def thumbnail(self, image_path: str, output_path: str, size: int) -> None:

467

"""

468

Create square thumbnail of image.

469

470

Parameters:

471

- image_path: Source image file path

472

- output_path: Destination image file path

473

- size: Thumbnail size (width and height)

474

"""

475

```

476

477

### Image Format Conversion

478

479

```python

480

from beets.util.artresizer import ArtResizer

481

482

def convert_artwork(src_path, dest_path, max_size=500):

483

"""Convert and resize artwork for embedding."""

484

485

resizer = ArtResizer(quality=90)

486

487

try:

488

# Create thumbnail

489

resizer.thumbnail(src_path, dest_path, max_size)

490

print(f"Created artwork: {dest_path}")

491

492

except Exception as e:

493

print(f"Artwork conversion failed: {e}")

494

raise

495

```

496

497

## Pipeline Processing

498

499

### Parallel Processing Utilities

500

501

```python { .api }

502

class Pipeline:

503

"""Parallel processing pipeline for import operations."""

504

505

def __init__(self, stages: List[Callable]):

506

"""

507

Initialize processing pipeline.

508

509

Parameters:

510

- stages: List of processing functions

511

"""

512

513

def run_parallel(self, items: List[Any], threads: int = 4) -> List[Any]:

514

"""

515

Process items through pipeline stages in parallel.

516

517

Parameters:

518

- items: List of items to process

519

- threads: Number of worker threads

520

521

Returns:

522

List of processed items

523

"""

524

```

525

526

### Processing Example

527

528

```python

529

from beets.util.pipeline import Pipeline

530

531

def process_import_batch(items):

532

"""Process a batch of items through parallel pipeline."""

533

534

def load_metadata(item):

535

"""Load metadata from file."""

536

item.load()

537

return item

538

539

def tag_item(item):

540

"""Apply automatic tagging."""

541

# Tagging logic here

542

return item

543

544

def write_tags(item):

545

"""Write tags back to file."""

546

item.write()

547

return item

548

549

# Create processing pipeline

550

pipeline = Pipeline([load_metadata, tag_item, write_tags])

551

552

# Process items in parallel

553

processed = pipeline.run_parallel(items, threads=4)

554

555

return processed

556

```

557

558

## Error Handling

559

560

```python { .api }

561

class FileOperationError(Exception):

562

"""Raised when file operations fail."""

563

564

def __init__(self, operation: str, path: str, error: Exception):

565

"""

566

Initialize file operation error.

567

568

Parameters:

569

- operation: Operation that failed ('move', 'copy', etc.)

570

- path: File path involved in operation

571

- error: Underlying exception

572

"""

573

574

class HumanReadableError(Exception):

575

"""Base for errors with human-readable messages."""

576

577

def log(self, logger) -> None:

578

"""Log error with appropriate level and formatting."""

579

```

580

581

### Error Handling Examples

582

583

```python

584

from beets.util import FileOperationError, displayable_path

585

586

def handle_file_errors(operation_func, *args):

587

"""Wrapper for file operations with error handling."""

588

589

try:

590

return operation_func(*args)

591

592

except FileOperationError as e:

593

print(f"File operation failed: {e}")

594

print(f"Path: {displayable_path(e.path)}")

595

raise

596

597

except PermissionError as e:

598

print(f"Permission denied: {displayable_path(e.filename)}")

599

raise

600

601

except OSError as e:

602

print(f"System error: {e}")

603

raise

604

```

605

606

This comprehensive utilities system provides the foundation for all file operations, path manipulation, and template formatting in beets, ensuring cross-platform compatibility and robust error handling.