0
# Python Utilities (epy)
1
2
Collection of general-purpose Python utilities including environment detection, iteration helpers, text processing, error handling, lazy imports, and language feature enhancements for robust Python development.
3
4
## Capabilities
5
6
### Environment Detection
7
8
Utilities for detecting the current Python execution environment.
9
10
```python { .api }
11
def is_notebook() -> bool:
12
"""
13
Detect if running in a Jupyter notebook environment.
14
15
Returns:
16
True if running in Jupyter notebook, False otherwise
17
"""
18
19
def is_test() -> bool:
20
"""
21
Detect if running in a test environment.
22
23
Returns:
24
True if running in test context, False otherwise
25
"""
26
```
27
28
### Import Management
29
30
Advanced import utilities for lazy loading and module management.
31
32
```python { .api }
33
def lazy_imports(**modules) -> Any:
34
"""
35
Create lazy import proxy objects for deferred loading.
36
37
Args:
38
**modules: Module names and import paths
39
40
Returns:
41
Lazy import proxy object
42
43
Example:
44
np = lazy_imports(numpy='numpy')
45
# numpy only imported when first used
46
"""
47
48
def lazy_api_imports(**api_modules) -> Any:
49
"""
50
Create lazy API import utilities for modular APIs.
51
52
Args:
53
**api_modules: API module definitions
54
55
Returns:
56
Lazy API import system
57
"""
58
59
def binary_adhoc(module_path: str) -> Any:
60
"""
61
Import binary modules ad-hoc for specialized use cases.
62
63
Args:
64
module_path: Path to binary module
65
66
Returns:
67
Imported binary module
68
"""
69
```
70
71
### Iteration Utilities
72
73
Enhanced iteration functions for data processing.
74
75
```python { .api }
76
def groupby(
77
iterable: Iterable[T],
78
key: Callable[[T], K] | None = None
79
) -> dict[K, list[T]]:
80
"""
81
Group iterable elements by key function.
82
83
Args:
84
iterable: Items to group
85
key: Function to generate grouping key
86
87
Returns:
88
Dictionary mapping keys to grouped items
89
"""
90
91
def splitby(
92
iterable: Iterable[T],
93
condition: Callable[[T], bool]
94
) -> tuple[list[T], list[T]]:
95
"""
96
Split iterable into two lists based on condition.
97
98
Args:
99
iterable: Items to split
100
condition: Function returning True/False for each item
101
102
Returns:
103
Tuple of (items_matching, items_not_matching)
104
"""
105
106
def zip_dict(*dicts: dict) -> dict[Any, tuple]:
107
"""
108
Zip multiple dictionaries by matching keys.
109
110
Args:
111
*dicts: Dictionaries to zip together
112
113
Returns:
114
Dictionary with keys mapped to tuples of values
115
"""
116
```
117
118
### Language Feature Enhancements
119
120
Utilities for enhancing Python language features.
121
122
```python { .api }
123
def frozen(cls: type) -> type:
124
"""
125
Decorator to make class instances immutable.
126
127
Args:
128
cls: Class to make frozen
129
130
Returns:
131
Frozen class where instances cannot be modified
132
"""
133
134
def is_namedtuple(obj: Any) -> bool:
135
"""
136
Check if object is a named tuple.
137
138
Args:
139
obj: Object to check
140
141
Returns:
142
True if object is a named tuple, False otherwise
143
"""
144
145
def issubclass(obj: Any, base: type) -> bool:
146
"""
147
Safe subclass checking that handles non-class objects.
148
149
Args:
150
obj: Object to check
151
base: Base class to check against
152
153
Returns:
154
True if obj is subclass of base, False otherwise
155
"""
156
157
def wraps_cls(cls: type) -> Callable:
158
"""
159
Class-based version of functools.wraps.
160
161
Args:
162
cls: Class to wrap
163
164
Returns:
165
Decorator function for wrapping classes
166
"""
167
168
class StrEnum(str, Enum):
169
"""
170
String enumeration that inherits from both str and Enum.
171
172
Allows enum values to be used as strings while maintaining enum benefits.
173
"""
174
pass
175
```
176
177
### Context Management
178
179
Enhanced context management utilities.
180
181
```python { .api }
182
class ContextManager:
183
"""
184
Enhanced context manager with additional functionality.
185
"""
186
def __init__(self, enter_fn: Callable, exit_fn: Callable) -> None: ...
187
def __enter__(self) -> Any: ...
188
def __exit__(self, exc_type, exc_val, exc_tb) -> None: ...
189
190
class ExitStack:
191
"""
192
Enhanced exit stack for managing multiple context managers.
193
"""
194
def __init__(self) -> None: ...
195
def enter_context(self, cm: Any) -> Any: ...
196
def push(self, exit_fn: Callable) -> None: ...
197
def close(self) -> None: ...
198
```
199
200
### Error Handling
201
202
Utilities for enhanced error handling and exception management.
203
204
```python { .api }
205
def reraise(
206
exception: Exception,
207
message: str | None = None
208
) -> NoReturn:
209
"""
210
Reraise exception with optional additional message.
211
212
Args:
213
exception: Exception to reraise
214
message: Additional message to include
215
216
Raises:
217
The provided exception with enhanced context
218
"""
219
220
def maybe_reraise(
221
exception: Exception,
222
condition: bool,
223
message: str | None = None
224
) -> None:
225
"""
226
Conditionally reraise exception based on condition.
227
228
Args:
229
exception: Exception to potentially reraise
230
condition: Whether to reraise the exception
231
message: Additional message if reraising
232
233
Raises:
234
The provided exception if condition is True
235
"""
236
```
237
238
### Text Processing
239
240
Text manipulation and formatting utilities.
241
242
```python { .api }
243
def dedent(text: str) -> str:
244
"""
245
Remove leading whitespace from text while preserving relative indentation.
246
247
Args:
248
text: Text to dedent
249
250
Returns:
251
Dedented text
252
"""
253
254
def diff_str(str1: str, str2: str) -> str:
255
"""
256
Generate visual diff between two strings.
257
258
Args:
259
str1: First string
260
str2: Second string
261
262
Returns:
263
String showing differences between inputs
264
"""
265
266
def pprint(obj: Any, **kwargs) -> None:
267
"""
268
Pretty print object with enhanced formatting.
269
270
Args:
271
obj: Object to pretty print
272
**kwargs: Additional formatting options
273
"""
274
275
def pretty_repr(obj: Any) -> str:
276
"""
277
Generate pretty string representation of object.
278
279
Args:
280
obj: Object to represent
281
282
Returns:
283
Pretty string representation
284
"""
285
286
def pretty_repr_top_level(obj: Any) -> str:
287
"""
288
Generate pretty representation for top-level objects.
289
290
Args:
291
obj: Object to represent
292
293
Returns:
294
Top-level pretty representation
295
"""
296
297
class Lines:
298
"""
299
Utility class for manipulating text lines.
300
"""
301
def __init__(self, lines: list[str] | str) -> None: ...
302
def __str__(self) -> str: ...
303
def __len__(self) -> int: ...
304
def append(self, line: str) -> None: ...
305
def join(self, separator: str = '\\n') -> str: ...
306
```
307
308
### Regular Expression Utilities
309
310
Advanced regex utilities for text processing.
311
312
```python { .api }
313
def reverse_fstring(
314
template: str,
315
text: str
316
) -> dict[str, str]:
317
"""
318
Reverse f-string formatting to extract values.
319
320
Args:
321
template: F-string template (e.g., "Hello {name}, age {age}")
322
text: Formatted text (e.g., "Hello Alice, age 25")
323
324
Returns:
325
Dictionary of extracted values (e.g., {"name": "Alice", "age": "25"})
326
"""
327
```
328
329
### Utility Modules
330
331
Additional modules providing specialized functionality.
332
333
```python { .api }
334
_internal: ModuleType # Internal utilities module
335
typing: ModuleType # Enhanced typing utilities
336
testing: ModuleType # Testing utilities (when pytest available)
337
```
338
339
## Usage Examples
340
341
### Environment Detection
342
343
```python
344
from etils import epy
345
346
# Detect execution environment
347
if epy.is_notebook():
348
print("Running in Jupyter notebook")
349
# Use notebook-specific features
350
from IPython.display import display
351
else:
352
print("Running in regular Python")
353
354
if epy.is_test():
355
print("Running in test environment")
356
# Use test-specific configurations
357
DEBUG = True
358
else:
359
DEBUG = False
360
```
361
362
### Lazy Imports
363
364
```python
365
from etils import epy
366
367
# Lazy import expensive modules
368
lazy_modules = epy.lazy_imports(
369
np='numpy',
370
pd='pandas',
371
plt='matplotlib.pyplot'
372
)
373
374
# Modules only imported when first accessed
375
data = lazy_modules.np.array([1, 2, 3]) # numpy imported here
376
df = lazy_modules.pd.DataFrame(data) # pandas imported here
377
378
# Lazy API imports for modular systems
379
api = epy.lazy_api_imports(
380
vision='my_package.vision',
381
nlp='my_package.nlp'
382
)
383
```
384
385
### Iteration Utilities
386
387
```python
388
from etils import epy
389
390
# Group items by category
391
items = [
392
{'name': 'apple', 'type': 'fruit'},
393
{'name': 'carrot', 'type': 'vegetable'},
394
{'name': 'banana', 'type': 'fruit'},
395
{'name': 'broccoli', 'type': 'vegetable'}
396
]
397
398
grouped = epy.groupby(items, key=lambda x: x['type'])
399
# Result: {
400
# 'fruit': [{'name': 'apple', 'type': 'fruit'}, {'name': 'banana', 'type': 'fruit'}],
401
# 'vegetable': [{'name': 'carrot', 'type': 'vegetable'}, {'name': 'broccoli', 'type': 'vegetable'}]
402
# }
403
404
# Split by condition
405
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
406
evens, odds = epy.splitby(numbers, lambda x: x % 2 == 0)
407
# evens: [2, 4, 6, 8, 10], odds: [1, 3, 5, 7, 9]
408
409
# Zip dictionaries
410
dict1 = {'a': 1, 'b': 2, 'c': 3}
411
dict2 = {'a': 'x', 'b': 'y', 'c': 'z'}
412
zipped = epy.zip_dict(dict1, dict2)
413
# Result: {'a': (1, 'x'), 'b': (2, 'y'), 'c': (3, 'z')}
414
```
415
416
### Language Enhancements
417
418
```python
419
from etils import epy
420
421
# Create immutable classes
422
@epy.frozen
423
class Point:
424
def __init__(self, x, y):
425
self.x = x
426
self.y = y
427
428
point = Point(1, 2)
429
# point.x = 3 # Would raise error - object is frozen
430
431
# String enums
432
class Color(epy.StrEnum):
433
RED = 'red'
434
GREEN = 'green'
435
BLUE = 'blue'
436
437
# Can use as string or enum
438
color = Color.RED
439
print(f"Color: {color}") # Works as string
440
assert color == 'red' # String comparison works
441
442
# Safe subclass checking
443
class Animal: pass
444
class Dog(Animal): pass
445
446
assert epy.issubclass(Dog, Animal) # True
447
assert not epy.issubclass("not a class", Animal) # False, no error
448
449
# Named tuple detection
450
from collections import namedtuple
451
Person = namedtuple('Person', ['name', 'age'])
452
person = Person('Alice', 25)
453
454
assert epy.is_namedtuple(person) # True
455
assert not epy.is_namedtuple({'name': 'Bob'}) # False
456
```
457
458
### Error Handling
459
460
```python
461
from etils import epy
462
463
def risky_operation(data):
464
try:
465
return process_data(data)
466
except ValueError as e:
467
# Reraise with additional context
468
epy.reraise(e, "Failed to process data in risky_operation")
469
470
def conditional_error_handling(data, strict_mode=False):
471
try:
472
return validate_data(data)
473
except ValidationError as e:
474
# Only reraise in strict mode
475
epy.maybe_reraise(e, strict_mode, "Validation failed in strict mode")
476
return None # Return None if not strict
477
```
478
479
### Text Processing
480
481
```python
482
from etils import epy
483
484
# Dedent multi-line strings
485
code = '''
486
def hello():
487
print("Hello, world!")
488
return True
489
'''
490
clean_code = epy.dedent(code)
491
# Removes leading whitespace while preserving structure
492
493
# Pretty printing
494
complex_data = {
495
'model': {'layers': [64, 128, 256], 'dropout': 0.1},
496
'training': {'epochs': 100, 'batch_size': 32}
497
}
498
epy.pprint(complex_data) # Enhanced pretty printing
499
500
# String diffing
501
old_text = "The quick brown fox"
502
new_text = "The quick red fox"
503
diff = epy.diff_str(old_text, new_text)
504
print(diff) # Shows differences visually
505
506
# Reverse f-strings
507
template = "Hello {name}, you are {age} years old"
508
text = "Hello Alice, you are 25 years old"
509
values = epy.reverse_fstring(template, text)
510
# Result: {'name': 'Alice', 'age': '25'}
511
```
512
513
### Context Management
514
515
```python
516
from etils import epy
517
518
# Enhanced context manager
519
def my_context_manager():
520
print("Entering context")
521
try:
522
yield "context value"
523
finally:
524
print("Exiting context")
525
526
with epy.ContextManager(
527
enter_fn=lambda: print("Setup"),
528
exit_fn=lambda: print("Cleanup")
529
):
530
print("In context")
531
532
# Exit stack for multiple resources
533
with epy.ExitStack() as stack:
534
file1 = stack.enter_context(open('file1.txt'))
535
file2 = stack.enter_context(open('file2.txt'))
536
# Both files automatically closed
537
```
538
539
### Line Processing
540
541
```python
542
from etils import epy
543
544
# Work with text lines
545
lines = epy.Lines([
546
"Line 1",
547
"Line 2",
548
"Line 3"
549
])
550
551
lines.append("Line 4")
552
text = lines.join('\\n') # Join with newlines
553
print(f"Total lines: {len(lines)}")
554
555
# From string
556
content = "Line A\\nLine B\\nLine C"
557
lines = epy.Lines(content)
558
# Automatically splits on newlines
559
```