or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backends.mdcli.mdconfig.mdindex.mdpreprocessors.mdutils.md

utils.mddocs/

0

# Utility Functions

1

2

Foliant's utility module provides helper functions for plugin discovery, package management, output handling, and common operations across the Foliant ecosystem. These functions support the plugin architecture and provide consistent interfaces for system integration.

3

4

## Capabilities

5

6

### Plugin Discovery Functions

7

8

Functions for discovering and loading available Foliant extensions dynamically.

9

10

```python { .api }

11

def get_available_tags() -> Set[str]:

12

"""

13

Extract tags from all installed preprocessor classes.

14

Discovers all foliant.preprocessors.*.Preprocessor classes and

15

collects their tag attributes.

16

17

Returns:

18

Set[str]: Set of all available preprocessor tags

19

"""

20

21

def get_available_config_parsers() -> Dict[str, Type]:

22

"""

23

Get installed config parser submodules and their Parser classes.

24

Used for constructing the dynamic Foliant config parser.

25

26

Returns:

27

Dict[str, Type]: Dictionary mapping parser names to Parser classes

28

"""

29

30

def get_available_clis() -> Dict[str, Type]:

31

"""

32

Get installed CLI submodules and their Cli classes.

33

Used for constructing the dynamic Foliant CLI class.

34

35

Returns:

36

Dict[str, Type]: Dictionary mapping CLI names to Cli classes

37

"""

38

39

def get_available_backends() -> Dict[str, Tuple[str]]:

40

"""

41

Get installed backend submodules and their supported targets.

42

Used for backend validation and interactive selection.

43

44

Returns:

45

Dict[str, Tuple[str]]: Dictionary mapping backend names to target tuples

46

"""

47

```

48

49

### Package Management Functions

50

51

Functions for discovering and listing Foliant-related packages in the environment.

52

53

```python { .api }

54

def get_foliant_packages() -> List[str]:

55

"""

56

Get list of installed Foliant-related packages with versions.

57

Includes core foliant package and all foliantcontrib.* extensions.

58

59

Returns:

60

List[str]: Package names and versions (e.g., ['foliant 1.0.13', 'mkdocs 1.2.0'])

61

"""

62

```

63

64

### Output and Display Functions

65

66

Functions for consistent output handling and user interaction.

67

68

```python { .api }

69

def output(text: str, quiet=False):

70

"""

71

Output text to STDOUT in non-quiet mode.

72

Provides consistent output handling across Foliant.

73

74

Parameters:

75

- text (str): Message to output

76

- quiet (bool): Suppress output if True

77

"""

78

79

def spinner(text: str, logger: Logger, quiet=False, debug=False):

80

"""

81

Context manager for long-running operations with progress indication.

82

Shows spinner during operation and handles exceptions gracefully.

83

84

Parameters:

85

- text (str): Operation description to display

86

- logger (Logger): Logger for capturing errors

87

- quiet (bool): Hide messages if True

88

- debug (bool): Show full tracebacks on errors

89

90

Usage:

91

with spinner('Processing files', logger):

92

# Long-running operation here

93

process_files()

94

"""

95

```

96

97

### File System Utilities

98

99

Functions for temporary directory management and cleanup.

100

101

```python { .api }

102

def tmp(tmp_path: Path, keep_tmp=False):

103

"""

104

Context manager for temporary directory cleanup.

105

Removes directory before and after operation unless keep_tmp is True.

106

107

Parameters:

108

- tmp_path (Path): Path to temporary directory

109

- keep_tmp (bool): Skip cleanup if True

110

111

Usage:

112

with tmp(Path('./temp'), keep_tmp=False):

113

# Work with temporary files

114

create_temp_files()

115

# Directory automatically cleaned up

116

"""

117

```

118

119

## Usage Examples

120

121

### Plugin Discovery

122

123

```python

124

from foliant.utils import (

125

get_available_backends,

126

get_available_tags,

127

get_available_clis,

128

get_available_config_parsers

129

)

130

131

# Discover available backends

132

backends = get_available_backends()

133

print("Available backends:")

134

for name, targets in backends.items():

135

print(f" {name}: {', '.join(targets)}")

136

137

# Discover preprocessor tags

138

tags = get_available_tags()

139

print(f"Available tags: {', '.join(sorted(tags))}")

140

141

# Discover CLI extensions

142

clis = get_available_clis()

143

print(f"Available CLI commands: {', '.join(clis.keys())}")

144

145

# Discover config parsers

146

parsers = get_available_config_parsers()

147

print(f"Available config parsers: {', '.join(parsers.keys())}")

148

```

149

150

### Package Information

151

152

```python

153

from foliant.utils import get_foliant_packages

154

155

# Get installed Foliant packages

156

packages = get_foliant_packages()

157

print("Installed Foliant packages:")

158

for package in packages:

159

print(f" {package}")

160

161

# Example output:

162

# foliant 1.0.13

163

# mkdocs 1.4.2

164

# pandoc 2.1.0

165

# plantuml 1.2.1

166

```

167

168

### Output Handling

169

170

```python

171

from foliant.utils import output

172

173

# Standard output

174

output("Processing started...")

175

176

# Quiet mode (no output)

177

output("This won't be shown", quiet=True)

178

179

# Conditional output based on configuration

180

def process_with_output(files, quiet=False):

181

output(f"Processing {len(files)} files...", quiet)

182

for file in files:

183

output(f" Processing {file}", quiet)

184

output("Processing complete!", quiet)

185

```

186

187

### Progress Indication

188

189

```python

190

from foliant.utils import spinner

191

import logging

192

import time

193

194

logger = logging.getLogger('foliant')

195

196

# Basic spinner usage

197

with spinner('Loading configuration', logger):

198

time.sleep(2) # Long operation

199

# Prints: "Loading configuration... Done"

200

201

# Spinner with error handling

202

try:

203

with spinner('Generating PDF', logger, debug=True):

204

# Simulate operation that might fail

205

raise RuntimeError("PDF generation failed")

206

except RuntimeError:

207

print("Operation failed - check logs")

208

209

# Quiet mode (no visual output)

210

with spinner('Background processing', logger, quiet=True):

211

process_files() # Operation runs silently

212

```

213

214

### Temporary Directory Management

215

216

```python

217

from foliant.utils import tmp

218

from pathlib import Path

219

import shutil

220

221

# Automatic cleanup

222

with tmp(Path('./build_temp')):

223

# Create temporary files

224

temp_dir = Path('./build_temp')

225

temp_dir.mkdir(exist_ok=True)

226

227

(temp_dir / 'temp_file.txt').write_text('temporary content')

228

229

# Process files

230

process_temporary_files(temp_dir)

231

232

# Directory automatically cleaned up here

233

234

# Keep temporary files for debugging

235

with tmp(Path('./debug_temp'), keep_tmp=True):

236

# Create files for debugging

237

debug_dir = Path('./debug_temp')

238

debug_dir.mkdir(exist_ok=True)

239

240

create_debug_files(debug_dir)

241

242

# Directory preserved for inspection

243

```

244

245

### Dynamic Class Construction

246

247

```python

248

from foliant.utils import get_available_clis, get_available_config_parsers

249

250

# Create dynamic CLI class (how Foliant CLI works)

251

available_clis = get_available_clis()

252

253

class DynamicCli(*available_clis.values()):

254

"""Dynamic CLI combining all available commands."""

255

256

def __init__(self):

257

super().__init__()

258

print(f"Loaded {len(available_clis)} CLI extensions")

259

260

# Create dynamic config parser (how Foliant config works)

261

available_parsers = get_available_config_parsers()

262

263

class DynamicParser(*available_parsers.values()):

264

"""Dynamic parser combining all available parsers."""

265

pass

266

267

# Use dynamic classes

268

cli = DynamicCli()

269

parser = DynamicParser(Path('.'), 'foliant.yml', logging.getLogger())

270

```

271

272

### Error Handling with Spinner

273

274

```python

275

from foliant.utils import spinner

276

import logging

277

278

logger = logging.getLogger('foliant')

279

280

def safe_operation_with_spinner(operation_name, operation_func, *args, **kwargs):

281

"""Wrapper for operations with spinner and error handling."""

282

try:

283

with spinner(operation_name, logger, debug=True):

284

return operation_func(*args, **kwargs)

285

except Exception as e:

286

logger.error(f"{operation_name} failed: {e}")

287

return None

288

289

# Usage

290

result = safe_operation_with_spinner(

291

"Building documentation",

292

build_docs,

293

source_dir="./src",

294

output_dir="./build"

295

)

296

297

if result:

298

print(f"Build successful: {result}")

299

else:

300

print("Build failed - check logs")

301

```

302

303

### Backend Validation Utility

304

305

```python

306

from foliant.utils import get_available_backends

307

308

def validate_target_backend_combination(target: str, backend: str = None) -> tuple:

309

"""

310

Validate target/backend combination and suggest alternatives.

311

312

Returns:

313

tuple: (is_valid, suggested_backend_or_error_message)

314

"""

315

available = get_available_backends()

316

317

if backend:

318

# Validate specific backend

319

if backend not in available:

320

return False, f"Backend '{backend}' not available"

321

322

if target not in available[backend]:

323

return False, f"Backend '{backend}' doesn't support target '{target}'"

324

325

return True, backend

326

327

else:

328

# Find suitable backend

329

suitable = [name for name, targets in available.items() if target in targets]

330

331

if not suitable:

332

return False, f"No backend available for target '{target}'"

333

334

return True, suitable[0] # Return first suitable backend

335

336

# Usage

337

valid, result = validate_target_backend_combination('pdf', 'pandoc')

338

if valid:

339

print(f"Using backend: {result}")

340

else:

341

print(f"Error: {result}")

342

```