0
# Utilities
1
2
Core utilities for encryption, file operations, string processing, serialization, and output formatting. These utilities provide common functionality used throughout Tutor for security, data processing, and user interaction.
3
4
## Capabilities
5
6
### Encryption and Security
7
8
Password hashing and verification using bcrypt for secure credential storage.
9
10
```python { .api }
11
def encrypt(text: str) -> str:
12
"""
13
Encrypt text using bcrypt hashing for secure password storage.
14
15
Args:
16
text (str): Plain text to encrypt
17
18
Returns:
19
str: Bcrypt hash of the input text
20
"""
21
22
def verify_encrypted(encrypted: str, text: str) -> bool:
23
"""
24
Verify that plain text matches the encrypted hash.
25
26
Args:
27
encrypted (str): Previously encrypted hash
28
text (str): Plain text to verify
29
30
Returns:
31
bool: True if text matches the encrypted hash
32
"""
33
```
34
35
### File System Operations
36
37
Utilities for managing directories and file paths.
38
39
```python { .api }
40
def ensure_file_directory_exists(path: str) -> None:
41
"""
42
Create parent directory for a file path if it doesn't exist.
43
44
Args:
45
path (str): File path whose parent directory should be created
46
"""
47
48
def ensure_directory_exists(path: str) -> None:
49
"""
50
Create directory and any necessary parent directories.
51
52
Args:
53
path (str): Directory path to create
54
"""
55
```
56
57
### String and Data Processing
58
59
Utilities for generating random data and processing service configurations.
60
61
```python { .api }
62
def random_string(length: int) -> str:
63
"""
64
Generate a random string of specified length using alphanumeric characters.
65
66
Args:
67
length (int): Length of random string to generate
68
69
Returns:
70
str: Random alphanumeric string
71
"""
72
73
def list_if(services: List[Tuple[str, bool]]) -> str:
74
"""
75
Generate JSON array string of service names where the boolean is True.
76
77
Args:
78
services (List[Tuple[str, bool]]): List of (service_name, enabled) tuples
79
80
Returns:
81
str: JSON array string of enabled service names
82
"""
83
84
def common_domain(d1: str, d2: str) -> str:
85
"""
86
Find the common domain suffix between two domain names.
87
88
Args:
89
d1 (str): First domain name
90
d2 (str): Second domain name
91
92
Returns:
93
str: Common domain suffix
94
"""
95
96
def reverse_host(domain: str) -> str:
97
"""
98
Reverse domain name in Java package style (com.example.subdomain).
99
100
Args:
101
domain (str): Domain name to reverse
102
103
Returns:
104
str: Reversed domain name
105
"""
106
```
107
108
### System Information
109
110
Utilities for checking system state and user permissions.
111
112
```python { .api }
113
def is_root() -> bool:
114
"""
115
Check if the current process is running as root user.
116
117
Returns:
118
bool: True if running as root
119
"""
120
```
121
122
### YAML Serialization
123
124
YAML loading and saving with consistent formatting and multiple document support.
125
126
```python { .api }
127
def load(stream: Union[str, IO[str]]) -> Any:
128
"""
129
Load YAML content from string or file stream.
130
131
Args:
132
stream (Union[str, IO[str]]): YAML content string or file stream
133
134
Returns:
135
Any: Parsed YAML data
136
"""
137
138
def load_all(stream: str) -> Iterator[Any]:
139
"""
140
Load multiple YAML documents from string.
141
142
Args:
143
stream (str): YAML content string with multiple documents
144
145
Returns:
146
Iterator[Any]: Iterator of parsed YAML documents
147
"""
148
149
def dump_all(documents: Sequence[Any], fileobj: TextIOWrapper) -> None:
150
"""
151
Write multiple YAML documents to a file.
152
153
Args:
154
documents (Sequence[Any]): Sequence of objects to serialize
155
fileobj (TextIOWrapper): File object to write to
156
"""
157
158
def dump(content: Any, fileobj: TextIOWrapper) -> None:
159
"""
160
Write single YAML document to a file.
161
162
Args:
163
content (Any): Object to serialize
164
fileobj (TextIOWrapper): File object to write to
165
"""
166
167
def dumps(content: Any) -> str:
168
"""
169
Serialize object to YAML string.
170
171
Args:
172
content (Any): Object to serialize
173
174
Returns:
175
str: YAML string representation
176
"""
177
```
178
179
### String Formatting
180
181
Special formatting for configuration values and output display.
182
183
```python { .api }
184
def str_format(content: Any) -> str:
185
"""
186
Format values for string output with special handling for booleans and None.
187
188
Args:
189
content (Any): Value to format
190
191
Returns:
192
str: Formatted string (booleans as lowercase, None as empty string)
193
"""
194
```
195
196
### Output Formatting and Styling
197
198
Console output styling with colors and semantic formatting for different message types.
199
200
```python { .api }
201
def title(text: str) -> str:
202
"""
203
Format text as a title with green styling.
204
205
Args:
206
text (str): Text to format
207
208
Returns:
209
str: Styled title text
210
"""
211
212
def info(text: str) -> str:
213
"""
214
Format text as informational message in blue.
215
216
Args:
217
text (str): Text to format
218
219
Returns:
220
str: Styled info text
221
"""
222
223
def error(text: str) -> str:
224
"""
225
Format text as error message in red.
226
227
Args:
228
text (str): Text to format
229
230
Returns:
231
str: Styled error text
232
"""
233
234
def command(text: str) -> str:
235
"""
236
Format text as command in magenta.
237
238
Args:
239
text (str): Text to format
240
241
Returns:
242
str: Styled command text
243
"""
244
245
def question(text: str) -> str:
246
"""
247
Format text as question in yellow.
248
249
Args:
250
text (str): Text to format
251
252
Returns:
253
str: Styled question text
254
"""
255
256
def alert(text: str) -> str:
257
"""
258
Format text as alert with warning emoji.
259
260
Args:
261
text (str): Text to format
262
263
Returns:
264
str: Styled alert text with emoji
265
"""
266
```
267
268
### Bind Mount Management
269
270
Functions for parsing and managing bind mounts between host and container directories.
271
272
```python { .api }
273
def get_mounts(config: Config) -> list[str]:
274
"""
275
Get the list of bind mounts from configuration.
276
277
Args:
278
config (Config): Configuration dictionary
279
280
Returns:
281
list[str]: List of mount specifications
282
"""
283
284
def iter_mounts(user_mounts: list[str], *names: str) -> Iterable[str]:
285
"""
286
Iterate bind-mounts available to given compose services.
287
288
Args:
289
user_mounts (list[str]): List of user mount specifications
290
*names (str): Service names to filter mounts for
291
292
Yields:
293
str: Mount strings in "host_path:container_path" format
294
"""
295
296
def parse_mount(value: str) -> list[tuple[str, str, str]]:
297
"""
298
Parse mount argument into (service, host_path, container_path) tuples.
299
300
Supports explicit format: "service1,service2:/host/path:/container/path"
301
And implicit format: "/host/path" (uses COMPOSE_MOUNTS filter)
302
303
Args:
304
value (str): Mount specification string
305
306
Returns:
307
list[tuple[str, str, str]]: List of (service, host_path, container_path)
308
"""
309
```
310
311
### YAML Serialization
312
313
YAML parsing and serialization utilities using PyYAML SafeLoader.
314
315
```python { .api }
316
def load(stream: Union[str, IO[str]]) -> Any:
317
"""
318
Load YAML from string or file stream.
319
320
Args:
321
stream (Union[str, IO[str]]): YAML content or file stream
322
323
Returns:
324
Any: Parsed YAML content
325
"""
326
327
def load_all(stream: str) -> Iterator[Any]:
328
"""
329
Load multiple YAML documents from string.
330
331
Args:
332
stream (str): YAML content with multiple documents
333
334
Yields:
335
Any: Each parsed YAML document
336
"""
337
338
def dump(content: Any, fileobj: TextIOWrapper) -> None:
339
"""
340
Write content as YAML to file stream.
341
342
Args:
343
content (Any): Content to serialize
344
fileobj (TextIOWrapper): File stream to write to
345
"""
346
347
def dumps(content: Any) -> str:
348
"""
349
Serialize content to YAML string.
350
351
Args:
352
content (Any): Content to serialize
353
354
Returns:
355
str: YAML string representation
356
"""
357
358
def parse_key_value(text: str) -> Optional[tuple[str, Any]]:
359
"""
360
Parse KEY=YAML_VALUE command line arguments.
361
362
Args:
363
text (str): Command line argument string
364
365
Returns:
366
Optional[tuple[str, Any]]: (key, parsed_value) or None if invalid
367
"""
368
```
369
370
### Output Functions
371
372
Console output functions that combine styling with printing.
373
374
```python { .api }
375
def echo_info(text: str) -> None:
376
"""
377
Print informational message to stdout.
378
379
Args:
380
text (str): Message to print
381
"""
382
383
def echo_error(text: str) -> None:
384
"""
385
Print error message to stderr.
386
387
Args:
388
text (str): Error message to print
389
"""
390
391
def echo_alert(text: str) -> None:
392
"""
393
Print alert message to stderr.
394
395
Args:
396
text (str): Alert message to print
397
"""
398
399
def echo(text: str, err: bool = False) -> None:
400
"""
401
Print text to stdout or stderr.
402
403
Args:
404
text (str): Text to print
405
err (bool): Whether to print to stderr instead of stdout
406
"""
407
```
408
409
## Type Definitions
410
411
### Configuration Types
412
413
Core type definitions for configuration management.
414
415
```python { .api }
416
# Union type for all possible configuration values
417
ConfigValue = Union[str, float, None, bool, List[str], List[Any], Dict[str, Any], Dict[Any, Any]]
418
419
# Configuration dictionary type
420
Config = Dict[str, ConfigValue]
421
422
def cast_config(config: Any) -> Config:
423
"""
424
Validate and cast input to Config type.
425
426
Args:
427
config (Any): Input to validate as configuration
428
429
Returns:
430
Config: Validated configuration dictionary
431
432
Raises:
433
TutorError: If input is not a valid configuration
434
"""
435
436
def get_typed(config: dict, key: str, expected_type: type[T], default: Optional[T] = None) -> T:
437
"""
438
Get typed value from configuration dictionary with default fallback.
439
440
Args:
441
config (dict): Configuration dictionary
442
key (str): Configuration key to retrieve
443
expected_type (type[T]): Expected type of the value
444
default (Optional[T]): Default value if key is missing
445
446
Returns:
447
T: Typed configuration value
448
449
Raises:
450
TutorError: If value exists but is not of expected type
451
"""
452
```
453
454
### Exception Types
455
456
Base exception class for Tutor-specific errors.
457
458
```python { .api }
459
class TutorError(Exception):
460
"""
461
Base exception class for all Tutor-specific errors.
462
463
Used for configuration errors, deployment failures, plugin issues, etc.
464
"""
465
pass
466
```
467
468
## Usage Examples
469
470
### Encryption and Security
471
472
```python
473
from tutor.utils import encrypt, verify_encrypted
474
475
# Encrypt a password
476
password = "my_secure_password"
477
encrypted = encrypt(password)
478
479
# Later verify the password
480
is_valid = verify_encrypted(encrypted, "my_secure_password") # True
481
is_invalid = verify_encrypted(encrypted, "wrong_password") # False
482
```
483
484
### File Operations
485
486
```python
487
from tutor.utils import ensure_file_directory_exists, ensure_directory_exists
488
489
# Ensure parent directory exists before creating file
490
file_path = "/path/to/deep/directory/config.yml"
491
ensure_file_directory_exists(file_path)
492
493
# Create directory structure
494
ensure_directory_exists("/path/to/new/directory")
495
```
496
497
### String Processing
498
499
```python
500
from tutor.utils import random_string, list_if, common_domain, reverse_host
501
502
# Generate random password
503
password = random_string(32)
504
505
# Process service list
506
services = [("lms", True), ("cms", True), ("forum", False)]
507
enabled_services = list_if(services) # '["lms", "cms"]'
508
509
# Domain processing
510
common = common_domain("learn.example.com", "studio.example.com") # "example.com"
511
reversed = reverse_host("learn.example.com") # "com.example.learn"
512
```
513
514
### YAML Operations
515
516
```python
517
from tutor.serialize import load, dumps, dump
518
from tutor.utils import ensure_file_directory_exists
519
520
# Load YAML configuration
521
with open("config.yml", "r") as f:
522
config = load(f)
523
524
# Serialize to string
525
yaml_str = dumps({"key": "value", "list": [1, 2, 3]})
526
527
# Save to file
528
ensure_file_directory_exists("output/config.yml")
529
with open("output/config.yml", "w") as f:
530
dump(config, f)
531
```
532
533
### Output Formatting
534
535
```python
536
from tutor.fmt import echo_info, echo_error, echo_alert, title, error
537
538
# Print styled messages
539
echo_info("Configuration loaded successfully")
540
echo_error("Failed to connect to database")
541
echo_alert("Running as root is not recommended")
542
543
# Create styled strings
544
header = title("Open edX Platform Status")
545
error_msg = error("Service unavailable")
546
print(header)
547
print(error_msg)
548
```
549
550
### Configuration Type Handling
551
552
```python
553
from tutor.types import cast_config, get_typed, TutorError
554
555
# Validate configuration
556
try:
557
config = cast_config({"PLATFORM_NAME": "My Platform", "HTTP_PORT": 80})
558
except TutorError as e:
559
print(f"Invalid configuration: {e}")
560
561
# Get typed values with defaults
562
platform_name = get_typed(config, "PLATFORM_NAME", str, "Default Platform")
563
http_port = get_typed(config, "HTTP_PORT", int, 8000)
564
debug_mode = get_typed(config, "DEBUG", bool, False)
565
```
566
567
### Working with Service Lists
568
569
```python
570
from tutor.utils import list_if
571
572
# Define services with their enabled status
573
services = [
574
("lms", True),
575
("cms", True),
576
("forum", False),
577
("discovery", True),
578
("ecommerce", False)
579
]
580
581
# Generate JSON list of enabled services
582
enabled_json = list_if(services)
583
print(enabled_json) # ["lms", "cms", "discovery"]
584
585
# Use in templates or configuration
586
config_update = {
587
"ENABLED_SERVICES": enabled_json
588
}
589
```
590
591
### Bind Mount Management
592
593
```python
594
from tutor.bindmount import get_mounts, iter_mounts, parse_mount
595
from tutor.types import Config
596
597
# Get mounts from configuration
598
config: Config = {"MOUNTS": ["lms:/host/code:/openedx/edx-platform", "/host/themes"]}
599
mounts = get_mounts(config)
600
601
# Parse specific mount strings
602
explicit_mount = parse_mount("lms,cms:/host/code:/openedx/edx-platform")
603
# Returns: [("lms", "/host/code", "/openedx/edx-platform"), ("cms", "/host/code", "/openedx/edx-platform")]
604
605
implicit_mount = parse_mount("/host/themes")
606
# Uses COMPOSE_MOUNTS filter to determine container paths
607
608
# Get mounts for specific services
609
user_mounts = ["lms:/host/code:/openedx/edx-platform", "/host/themes"]
610
lms_mounts = list(iter_mounts(user_mounts, "lms"))
611
```
612
613
### YAML Configuration Parsing
614
615
```python
616
from tutor.serialize import load, dumps, parse_key_value
617
import io
618
619
# Parse YAML string
620
yaml_content = """
621
platform:
622
name: "My Open edX"
623
debug: true
624
services:
625
- lms
626
- cms
627
"""
628
config = load(yaml_content)
629
630
# Parse command line arguments
631
key_value = parse_key_value("PLATFORM_NAME=My Custom Platform")
632
# Returns: ("PLATFORM_NAME", "My Custom Platform")
633
634
boolean_value = parse_key_value("DEBUG=true")
635
# Returns: ("DEBUG", True)
636
637
list_value = parse_key_value("SERVICES=[lms, cms, forum]")
638
# Returns: ("SERVICES", ["lms", "cms", "forum"])
639
640
# Serialize back to YAML
641
updated_config = {
642
"PLATFORM_NAME": "Updated Platform",
643
"HTTP_PORT": 8000,
644
"SERVICES": ["lms", "cms"]
645
}
646
yaml_string = dumps(updated_config)
647
```