0
# Utility Functions
1
2
Cement provides a comprehensive collection of utility functions for common operations including file system operations, shell command execution, configuration helpers, and testing utilities.
3
4
## Capabilities
5
6
### Miscellaneous Utilities
7
8
Collection of general-purpose utility functions for common operations.
9
10
```python { .api }
11
def init_defaults(*sections: str) -> Dict[str, Any]:
12
"""
13
Create a standard dictionary for application configuration defaults.
14
15
Creates a nested dictionary structure with the specified sections.
16
Commonly used for setting up application configuration defaults.
17
18
Args:
19
*sections: Section names to create in the defaults dictionary
20
21
Returns:
22
Dictionary with nested sections for configuration defaults
23
24
Example:
25
config = init_defaults('myapp', 'database')
26
# Returns: {'myapp': {}, 'database': {}}
27
"""
28
29
def minimal_logger(namespace: str) -> logging.Logger:
30
"""
31
Create a basic logger instance for framework use.
32
33
Creates a minimal logger with basic configuration suitable for
34
framework internal logging and simple application logging needs.
35
36
Args:
37
namespace: Logger namespace/name
38
39
Returns:
40
Configured Logger instance
41
"""
42
43
def rando(salt: str = None) -> str:
44
"""
45
Generate a random hash string for various purposes.
46
47
Useful for testing, unique identifiers, or any time a random
48
string is required. Uses SHA256 for compatibility.
49
50
Args:
51
salt: Optional salt string. If None, uses random.random()
52
53
Returns:
54
Random hash string (32 characters)
55
56
Example:
57
random_id = rando('my_salt_string')
58
test_id = rando() # Uses random salt
59
"""
60
61
def is_true(item: Any) -> bool:
62
"""
63
Test if a value should be considered True.
64
65
Provides consistent boolean evaluation for configuration values
66
and user input, handling strings like 'true', 'yes', '1', etc.
67
68
Args:
69
item: Value to test for truthiness
70
71
Returns:
72
True if item should be considered True, False otherwise
73
74
Example:
75
is_true('true') # True
76
is_true('yes') # True
77
is_true('1') # True
78
is_true('false') # False
79
is_true(0) # False
80
"""
81
82
def wrap(text: str, width: int = 77, indent: str = '', long_words: bool = False, hyphens: bool = False) -> str:
83
"""
84
Wrap text to specified width with optional indentation.
85
86
Provides text wrapping functionality with control over line width,
87
indentation, and handling of long words and hyphenation.
88
89
Args:
90
text: Text to wrap
91
width: Maximum line width
92
indent: String to indent each line
93
long_words: Whether to break long words
94
hyphens: Whether to use hyphenation
95
96
Returns:
97
Wrapped text string
98
"""
99
100
def get_random_string(length: int = 12) -> str:
101
"""
102
Generate a random string of specified length.
103
104
Creates random strings suitable for temporary identifiers,
105
test data, or other purposes requiring random text.
106
107
Args:
108
length: Length of random string to generate
109
110
Returns:
111
Random string of specified length
112
"""
113
```
114
115
### File System Utilities
116
117
File system operations and temporary file/directory management.
118
119
```python { .api }
120
class Tmp:
121
"""
122
Context manager for temporary directory and file creation with cleanup.
123
124
Provides creation and automatic cleanup of temporary directories and files.
125
Designed to be used with the 'with' statement for automatic resource management.
126
"""
127
128
def __init__(self, cleanup: bool = True, suffix: str = '', prefix: str = '', dir: str = None) -> None:
129
"""
130
Initialize temporary file/directory manager.
131
132
Args:
133
cleanup: Whether to delete temp directory/file on exit
134
suffix: Suffix for temp directory and file names
135
prefix: Prefix for temp directory and file names
136
dir: Parent directory for temp directory/file creation
137
"""
138
139
def __enter__(self) -> 'Tmp':
140
"""
141
Enter context manager and create temporary directory/file.
142
143
Returns:
144
Self with populated dir and file attributes
145
"""
146
147
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
148
"""
149
Exit context manager and cleanup temporary directory/file if requested.
150
151
Args:
152
exc_type: Exception type (if any)
153
exc_val: Exception value (if any)
154
exc_tb: Exception traceback (if any)
155
"""
156
157
@property
158
def dir(self) -> str:
159
"""Path to the temporary directory."""
160
161
@property
162
def file(self) -> str:
163
"""Path to the temporary file."""
164
165
def abspath(path: str) -> str:
166
"""
167
Get absolute path with user directory expansion.
168
169
Expands user directory (~) and converts to absolute path.
170
171
Args:
172
path: File or directory path
173
174
Returns:
175
Absolute path string
176
"""
177
178
def backup(path: str, suffix: str = '.bak') -> str:
179
"""
180
Create a backup copy of a file or directory.
181
182
Creates a backup copy with the specified suffix. If backup
183
already exists, appends a number to make it unique.
184
185
Args:
186
path: Path to file or directory to backup
187
suffix: Suffix to append to backup name
188
189
Returns:
190
Path to created backup
191
"""
192
193
def cleanup(*paths: str) -> None:
194
"""
195
Remove files and directories.
196
197
Safely removes the specified files and directories,
198
handling both files and directories recursively.
199
200
Args:
201
*paths: Paths to files/directories to remove
202
"""
203
```
204
205
### Shell Utilities
206
207
Shell command execution and process management utilities.
208
209
```python { .api }
210
def cmd(command: str, capture: bool = True, *args: Any, **kwargs: Any) -> Union[Tuple[str, str, int], int]:
211
"""
212
Execute shell command with optional output capture.
213
214
Wrapper around exec_cmd and exec_cmd2 that provides a unified
215
interface for shell command execution with flexible output handling.
216
217
Args:
218
command: Command string to execute
219
capture: Whether to capture and return output
220
*args: Additional arguments passed to Popen
221
**kwargs: Additional keyword arguments passed to Popen
222
223
Returns:
224
Tuple of (stdout, stderr, return_code) if capture=True
225
Return code only if capture=False
226
227
Example:
228
# Capture output
229
out, err, code = cmd('ls -la')
230
231
# Just get return code
232
code = cmd('touch /tmp/test', capture=False)
233
"""
234
235
def exec_cmd(command: str, *args: Any, **kwargs: Any) -> Tuple[str, str, int]:
236
"""
237
Execute command and capture output.
238
239
Executes shell command and captures both stdout and stderr,
240
returning all output along with the exit code.
241
242
Args:
243
command: Command string to execute
244
*args: Additional arguments passed to Popen
245
**kwargs: Additional keyword arguments passed to Popen
246
247
Returns:
248
Tuple of (stdout, stderr, return_code)
249
"""
250
251
def exec_cmd2(command: str, *args: Any, **kwargs: Any) -> int:
252
"""
253
Execute command without capturing output.
254
255
Executes shell command allowing output to go directly to
256
console, returning only the exit code.
257
258
Args:
259
command: Command string to execute
260
*args: Additional arguments passed to Popen
261
**kwargs: Additional keyword arguments passed to Popen
262
263
Returns:
264
Command exit code
265
"""
266
267
class Prompt:
268
"""
269
Interactive prompting utilities for user input.
270
271
Provides methods for prompting users for various types of input
272
including text, boolean choices, and multiple choice selections.
273
"""
274
275
def prompt(self, text: str, default: str = None) -> str:
276
"""
277
Prompt user for text input.
278
279
Args:
280
text: Prompt text to display
281
default: Default value if user enters nothing
282
283
Returns:
284
User input string or default value
285
"""
286
287
def prompt_bool(self, text: str, default: bool = None) -> bool:
288
"""
289
Prompt user for boolean (yes/no) input.
290
291
Args:
292
text: Prompt text to display
293
default: Default boolean value
294
295
Returns:
296
Boolean value based on user input
297
"""
298
299
def prompt_options(self, text: str, options: List[str], default: str = None) -> str:
300
"""
301
Prompt user to select from multiple options.
302
303
Args:
304
text: Prompt text to display
305
options: List of available options
306
default: Default option if user enters nothing
307
308
Returns:
309
Selected option string
310
"""
311
```
312
313
### Testing Utilities
314
315
Utilities specifically designed for testing cement applications.
316
317
```python { .api }
318
class TestApp(App):
319
"""
320
Application class designed for testing cement applications.
321
322
Extends the base App class with testing-specific functionality
323
and simplified configuration for test environments.
324
"""
325
326
def with_app(app_class: Type[App] = None, **app_kwargs: Any) -> Callable:
327
"""
328
Decorator for testing functions that need an application instance.
329
330
Provides a cement application instance to test functions,
331
handling setup and cleanup automatically.
332
333
Args:
334
app_class: Application class to instantiate (defaults to TestApp)
335
**app_kwargs: Keyword arguments passed to app constructor
336
337
Returns:
338
Decorator function that injects app instance
339
340
Example:
341
@with_app()
342
def test_my_function(app):
343
app.setup()
344
# Test code here
345
app.run()
346
"""
347
```
348
349
### Version Utilities
350
351
Version information and management utilities.
352
353
```python { .api }
354
def get_version() -> str:
355
"""
356
Get the cement framework version string.
357
358
Returns the current version of the cement framework.
359
360
Returns:
361
Version string (e.g., '3.0.14')
362
"""
363
364
VERSION: Tuple[int, int, int] = (3, 0, 14)
365
"""Framework version tuple (major, minor, patch)"""
366
```
367
368
## Usage Examples
369
370
### File System Operations
371
372
```python
373
from cement.utils import fs
374
import os
375
376
# Using temporary directory context manager
377
with fs.Tmp() as tmp:
378
print(f"Temp directory: {tmp.dir}")
379
print(f"Temp file: {tmp.file}")
380
381
# Create some files in temp directory
382
test_file = os.path.join(tmp.dir, 'test.txt')
383
with open(test_file, 'w') as f:
384
f.write('Hello World!')
385
386
# Use temp file
387
with open(tmp.file, 'w') as f:
388
f.write('Temporary data')
389
390
# Directory and file are automatically cleaned up on exit
391
392
# Working with paths
393
home_config = fs.abspath('~/.myapp.conf')
394
print(f"Config path: {home_config}")
395
396
# Creating backups
397
original_file = '/path/to/important.txt'
398
if os.path.exists(original_file):
399
backup_path = fs.backup(original_file)
400
print(f"Created backup: {backup_path}")
401
402
# Cleanup multiple paths
403
fs.cleanup('/tmp/file1.txt', '/tmp/file2.txt', '/tmp/test_dir')
404
```
405
406
### Shell Command Execution
407
408
```python
409
from cement.utils import shell
410
411
# Execute command and capture output
412
stdout, stderr, exit_code = shell.cmd('ls -la /tmp')
413
print(f"Output: {stdout}")
414
print(f"Errors: {stderr}")
415
print(f"Exit code: {exit_code}")
416
417
# Execute command without capturing output (output goes to console)
418
exit_code = shell.cmd('echo "Hello World"', capture=False)
419
print(f"Command completed with exit code: {exit_code}")
420
421
# Using exec_cmd directly
422
out, err, code = shell.exec_cmd('ps aux | grep python')
423
if code == 0:
424
print("Python processes found:")
425
print(out)
426
427
# Using exec_cmd2 for commands that need user interaction
428
exit_code = shell.exec_cmd2('vim /tmp/test.txt') # Opens vim directly
429
```
430
431
### Interactive Prompting
432
433
```python
434
from cement.utils.shell import Prompt
435
436
prompt = Prompt()
437
438
# Basic text input
439
name = prompt.prompt('Enter your name: ', default='Anonymous')
440
print(f"Hello, {name}!")
441
442
# Boolean input
443
confirm = prompt.prompt_bool('Continue? [y/N]: ', default=False)
444
if confirm:
445
print("Continuing...")
446
else:
447
print("Aborted.")
448
449
# Multiple choice
450
options = ['production', 'staging', 'development']
451
env = prompt.prompt_options('Select environment: ', options, default='development')
452
print(f"Selected environment: {env}")
453
```
454
455
### Configuration Helpers
456
457
```python
458
from cement.utils.misc import init_defaults, is_true, rando
459
460
# Initialize configuration structure
461
config = init_defaults('app', 'database', 'logging')
462
config['app']['debug'] = False
463
config['app']['name'] = 'MyApp'
464
config['database']['host'] = 'localhost'
465
config['database']['port'] = 5432
466
config['logging']['level'] = 'INFO'
467
468
print(f"Config structure: {config}")
469
470
# Test boolean values from various sources
471
user_input = 'yes'
472
debug_enabled = is_true(user_input)
473
print(f"Debug enabled: {debug_enabled}")
474
475
# Generate random identifiers
476
session_id = rando('user_session')
477
test_id = rando()
478
print(f"Session ID: {session_id}")
479
print(f"Test ID: {test_id}")
480
```
481
482
### Testing Utilities
483
484
```python
485
from cement.utils.test import TestApp, with_app
486
from cement import Controller, ex
487
488
class TestController(Controller):
489
class Meta:
490
label = 'test'
491
492
@ex(help='test command')
493
def hello(self):
494
return 'Hello from test!'
495
496
# Using TestApp directly
497
def test_app_functionality():
498
with TestApp() as app:
499
app.setup()
500
assert app.config.get('myapp', 'debug') == True
501
app.run()
502
503
# Using with_app decorator
504
@with_app(TestApp, handlers=[TestController])
505
def test_controller(app):
506
app.setup()
507
508
# Test controller registration
509
assert 'test' in app.handler.list('controller')
510
511
# Get controller instance
512
controller = app.handler.get('controller', 'test')(app)
513
result = controller.hello()
514
assert result == 'Hello from test!'
515
516
# Run tests
517
test_app_functionality()
518
test_controller()
519
```
520
521
### Text Processing
522
523
```python
524
from cement.utils.misc import wrap, get_random_string
525
526
# Text wrapping
527
long_text = "This is a very long line of text that needs to be wrapped to fit within specific line length constraints for better readability and formatting."
528
529
wrapped = wrap(long_text, width=50, indent=' ')
530
print("Wrapped text:")
531
print(wrapped)
532
533
# Generate random strings for testing
534
test_data = []
535
for i in range(5):
536
random_str = get_random_string(8)
537
test_data.append(f"test_{random_str}")
538
539
print(f"Test data: {test_data}")
540
```
541
542
### Version Information
543
544
```python
545
from cement.utils.version import get_version, VERSION
546
547
# Get version information
548
version_string = get_version()
549
print(f"Cement version: {version_string}")
550
551
major, minor, patch = VERSION
552
print(f"Version components: {major}.{minor}.{patch}")
553
554
# Version comparison
555
if VERSION >= (3, 0, 0):
556
print("Using cement 3.0+")
557
```