0
# Configuration and Settings
1
2
dill provides global configuration options and settings that control serialization behavior, protocol selection, and various operational modes for optimal performance and compatibility.
3
4
## Global Settings
5
6
### Settings Dictionary
7
8
```python { .api }
9
# Global settings dictionary
10
settings = {
11
'protocol': DEFAULT_PROTOCOL, # int: Default pickle protocol version
12
'byref': False, # bool: Pickle by reference when possible
13
'fmode': 0, # int: File mode setting (0=HANDLE_FMODE, 1=CONTENTS_FMODE, 2=FILE_FMODE)
14
'recurse': False, # bool: Recursively pickle nested objects
15
'ignore': False # bool: Ignore certain errors during serialization
16
}
17
```
18
19
### Protocol Constants
20
21
```python { .api }
22
DEFAULT_PROTOCOL = 3 # Default pickle protocol version
23
HIGHEST_PROTOCOL = 5 # Highest supported pickle protocol version
24
25
# File mode constants
26
HANDLE_FMODE = 0 # Preserve file handles during serialization
27
CONTENTS_FMODE = 1 # Save file contents instead of handles
28
FILE_FMODE = 2 # Save file metadata and paths
29
```
30
31
### Extension Control
32
33
```python { .api }
34
def extend(use_dill=True):
35
"""
36
Add or remove dill types to/from the pickle registry.
37
38
Controls whether dill's extended types are available in the standard
39
pickle module's dispatch table for backward compatibility.
40
41
Parameters:
42
- use_dill: bool, if True extend dispatch table with dill types,
43
if False revert to standard pickle types only
44
45
Returns:
46
None
47
"""
48
```
49
50
## Usage Examples
51
52
### Basic Configuration
53
54
```python
55
import dill
56
57
# Check current settings
58
print("Current settings:")
59
for key, value in dill.settings.items():
60
print(f" {key}: {value}")
61
62
# Modify settings
63
original_protocol = dill.settings['protocol']
64
dill.settings['protocol'] = dill.HIGHEST_PROTOCOL
65
dill.settings['byref'] = True
66
67
print(f"\\nChanged protocol from {original_protocol} to {dill.settings['protocol']}")
68
print(f"Enabled byref: {dill.settings['byref']}")
69
70
# Test with new settings
71
def test_function():
72
return "Hello with new settings!"
73
74
serialized = dill.dumps(test_function)
75
restored = dill.loads(serialized)
76
print(f"Function result: {restored()}")
77
78
# Restore original settings
79
dill.settings['protocol'] = original_protocol
80
dill.settings['byref'] = False
81
```
82
83
### Protocol Selection and Performance
84
85
```python
86
import dill
87
import time
88
89
def benchmark_protocols(obj, protocols=None):
90
"""Benchmark different pickle protocols."""
91
if protocols is None:
92
protocols = [0, 1, 2, 3, 4, dill.HIGHEST_PROTOCOL]
93
94
results = {}
95
96
for protocol in protocols:
97
try:
98
# Set protocol
99
original_protocol = dill.settings['protocol']
100
dill.settings['protocol'] = protocol
101
102
# Time serialization
103
start_time = time.time()
104
serialized = dill.dumps(obj)
105
serialize_time = time.time() - start_time
106
107
# Time deserialization
108
start_time = time.time()
109
dill.loads(serialized)
110
deserialize_time = time.time() - start_time
111
112
results[protocol] = {
113
'serialize_time': serialize_time,
114
'deserialize_time': deserialize_time,
115
'size': len(serialized),
116
'total_time': serialize_time + deserialize_time
117
}
118
119
# Restore original protocol
120
dill.settings['protocol'] = original_protocol
121
122
except Exception as e:
123
results[protocol] = {'error': str(e)}
124
125
return results
126
127
# Test with complex object
128
complex_obj = {
129
'functions': [lambda x: x**i for i in range(5)],
130
'data': list(range(1000)),
131
'nested': {'level1': {'level2': [i*j for i in range(10) for j in range(10)]}}
132
}
133
134
protocol_results = benchmark_protocols(complex_obj)
135
136
print("Protocol Performance Comparison:")
137
print("-" * 50)
138
for protocol, result in protocol_results.items():
139
if 'error' in result:
140
print(f"Protocol {protocol}: ERROR - {result['error']}")
141
else:
142
print(f"Protocol {protocol}:")
143
print(f" Serialize: {result['serialize_time']:.4f}s")
144
print(f" Deserialize: {result['deserialize_time']:.4f}s")
145
print(f" Size: {result['size']:,} bytes")
146
print(f" Total: {result['total_time']:.4f}s")
147
```
148
149
### File Mode Configuration
150
151
```python
152
import dill
153
import tempfile
154
import io
155
156
def demonstrate_file_modes():
157
"""Demonstrate different file mode behaviors."""
158
159
# Create object with file handle
160
temp_file = tempfile.NamedTemporaryFile(mode='w+', delete=False)
161
temp_file.write("Test data for file mode demonstration")
162
temp_file.seek(0)
163
164
obj_with_file = {
165
'file_handle': temp_file,
166
'file_path': temp_file.name,
167
'data': [1, 2, 3, 4, 5]
168
}
169
170
# Test different file modes
171
modes = {
172
dill.HANDLE_FMODE: "HANDLE_FMODE (preserve handles)",
173
dill.CONTENTS_FMODE: "CONTENTS_FMODE (save contents)",
174
dill.FILE_FMODE: "FILE_FMODE (save metadata)"
175
}
176
177
for fmode, description in modes.items():
178
print(f"\\nTesting {description}:")
179
print("-" * 40)
180
181
try:
182
# Set file mode
183
original_fmode = dill.settings['fmode']
184
dill.settings['fmode'] = fmode
185
186
# Serialize
187
serialized = dill.dumps(obj_with_file)
188
print(f"✓ Serialization successful ({len(serialized)} bytes)")
189
190
# Deserialize
191
restored = dill.loads(serialized)
192
print(f"✓ Deserialization successful")
193
194
# Test restored file behavior
195
if 'file_handle' in restored:
196
file_obj = restored['file_handle']
197
if hasattr(file_obj, 'read'):
198
try:
199
content = file_obj.read()
200
print(f"✓ File content: {repr(content[:50])}")
201
except:
202
print("✗ File handle not functional")
203
else:
204
print("○ File handle replaced with alternative representation")
205
206
# Restore original setting
207
dill.settings['fmode'] = original_fmode
208
209
except Exception as e:
210
print(f"✗ Failed: {e}")
211
212
# Cleanup
213
temp_file.close()
214
import os
215
os.unlink(temp_file.name)
216
217
demonstrate_file_modes()
218
```
219
220
### Reference vs Value Serialization
221
222
```python
223
import dill
224
225
def demonstrate_byref_setting():
226
"""Demonstrate byref setting effects."""
227
228
# Create shared object
229
shared_list = [1, 2, 3, 4, 5]
230
231
# Create objects that reference the shared list
232
obj1 = {'data': shared_list, 'name': 'object1'}
233
obj2 = {'data': shared_list, 'name': 'object2'}
234
container = {'obj1': obj1, 'obj2': obj2, 'shared': shared_list}
235
236
# Test with byref=False (default)
237
print("Testing with byref=False (value serialization):")
238
dill.settings['byref'] = False
239
240
serialized_value = dill.dumps(container)
241
restored_value = dill.loads(serialized_value)
242
243
# Modify shared list in restored object
244
restored_value['shared'].append(999)
245
246
print(f" Original obj1 data: {container['obj1']['data']}")
247
print(f" Restored obj1 data: {restored_value['obj1']['data']}")
248
print(f" Restored obj2 data: {restored_value['obj2']['data']}")
249
print(f" Objects share data in restored: {restored_value['obj1']['data'] is restored_value['obj2']['data']}")
250
251
# Test with byref=True
252
print("\\nTesting with byref=True (reference serialization):")
253
dill.settings['byref'] = True
254
255
serialized_ref = dill.dumps(container)
256
restored_ref = dill.loads(serialized_ref)
257
258
# Modify shared list in restored object
259
restored_ref['shared'].append(888)
260
261
print(f" Restored obj1 data: {restored_ref['obj1']['data']}")
262
print(f" Restored obj2 data: {restored_ref['obj2']['data']}")
263
print(f" Objects share data in restored: {restored_ref['obj1']['data'] is restored_ref['obj2']['data']}")
264
265
print(f"\\nSerialized sizes:")
266
print(f" Value mode: {len(serialized_value)} bytes")
267
print(f" Reference mode: {len(serialized_ref)} bytes")
268
269
# Restore default
270
dill.settings['byref'] = False
271
272
demonstrate_byref_setting()
273
```
274
275
### Recursive Serialization Control
276
277
```python
278
import dill
279
280
def demonstrate_recursive_setting():
281
"""Demonstrate recursive serialization setting."""
282
283
# Create nested structure with functions
284
def outer_function(x):
285
def inner_function(y):
286
def innermost_function(z):
287
return x + y + z
288
return innermost_function
289
return inner_function
290
291
nested_func = outer_function(10)
292
deeply_nested = nested_func(20)
293
294
test_obj = {
295
'outer': outer_function,
296
'nested': nested_func,
297
'deep': deeply_nested,
298
'data': {'level1': {'level2': {'level3': [1, 2, 3]}}}
299
}
300
301
# Test with recurse=False (default)
302
print("Testing with recurse=False:")
303
dill.settings['recurse'] = False
304
305
try:
306
serialized_no_recurse = dill.dumps(test_obj)
307
restored_no_recurse = dill.loads(serialized_no_recurse)
308
309
# Test functionality
310
result = restored_no_recurse['deep'](30)
311
print(f" ✓ Serialization successful, result: {result}")
312
print(f" Size: {len(serialized_no_recurse)} bytes")
313
except Exception as e:
314
print(f" ✗ Failed: {e}")
315
316
# Test with recurse=True
317
print("\\nTesting with recurse=True:")
318
dill.settings['recurse'] = True
319
320
try:
321
serialized_recurse = dill.dumps(test_obj)
322
restored_recurse = dill.loads(serialized_recurse)
323
324
# Test functionality
325
result = restored_recurse['deep'](30)
326
print(f" ✓ Serialization successful, result: {result}")
327
print(f" Size: {len(serialized_recurse)} bytes")
328
except Exception as e:
329
print(f" ✗ Failed: {e}")
330
331
# Restore default
332
dill.settings['recurse'] = False
333
334
demonstrate_recursive_setting()
335
```
336
337
## Advanced Configuration Management
338
339
### Configuration Context Manager
340
341
```python
342
import dill
343
from contextlib import contextmanager
344
345
@contextmanager
346
def dill_config(**settings):
347
"""Context manager for temporary dill configuration."""
348
# Save original settings
349
original = dill.settings.copy()
350
351
# Apply new settings
352
dill.settings.update(settings)
353
354
try:
355
yield dill.settings
356
finally:
357
# Restore original settings
358
dill.settings.clear()
359
dill.settings.update(original)
360
361
# Usage examples
362
def test_function():
363
return "Test result"
364
365
# Temporary high-performance configuration
366
with dill_config(protocol=dill.HIGHEST_PROTOCOL, byref=True):
367
print(f"Using protocol: {dill.settings['protocol']}")
368
serialized = dill.dumps(test_function)
369
print(f"Serialized size: {len(serialized)} bytes")
370
371
# Back to default settings
372
print(f"Back to protocol: {dill.settings['protocol']}")
373
374
# Temporary memory-efficient configuration
375
with dill_config(fmode=dill.CONTENTS_FMODE, recurse=True):
376
complex_obj = {'data': [1, 2, 3], 'func': lambda x: x*2}
377
serialized = dill.dumps(complex_obj)
378
restored = dill.loads(serialized)
379
print(f"Function result: {restored['func'](5)}")
380
```
381
382
### Configuration Profiles
383
384
```python
385
import dill
386
387
class DillProfile:
388
"""Predefined configuration profiles for different use cases."""
389
390
# Performance-optimized profile
391
PERFORMANCE = {
392
'protocol': dill.HIGHEST_PROTOCOL,
393
'byref': False,
394
'fmode': dill.HANDLE_FMODE,
395
'recurse': False,
396
'ignore': False
397
}
398
399
# Size-optimized profile
400
COMPACT = {
401
'protocol': dill.HIGHEST_PROTOCOL,
402
'byref': True,
403
'fmode': dill.CONTENTS_FMODE,
404
'recurse': True,
405
'ignore': False
406
}
407
408
# Compatibility profile
409
COMPATIBLE = {
410
'protocol': 2, # Widely supported protocol
411
'byref': False,
412
'fmode': dill.CONTENTS_FMODE,
413
'recurse': False,
414
'ignore': True
415
}
416
417
# Debug profile
418
DEBUG = {
419
'protocol': 0, # Human-readable protocol
420
'byref': False,
421
'fmode': dill.CONTENTS_FMODE,
422
'recurse': True,
423
'ignore': False
424
}
425
426
class ConfigManager:
427
"""Configuration management utility."""
428
429
def __init__(self):
430
self.saved_configs = {}
431
self.current_profile = None
432
433
def apply_profile(self, profile_name):
434
"""Apply a predefined profile."""
435
profiles = {
436
'performance': DillProfile.PERFORMANCE,
437
'compact': DillProfile.COMPACT,
438
'compatible': DillProfile.COMPATIBLE,
439
'debug': DillProfile.DEBUG
440
}
441
442
if profile_name in profiles:
443
# Save current config
444
self.save_config('pre_profile')
445
446
# Apply profile
447
dill.settings.update(profiles[profile_name])
448
self.current_profile = profile_name
449
450
print(f"Applied {profile_name} profile:")
451
for key, value in profiles[profile_name].items():
452
print(f" {key}: {value}")
453
else:
454
print(f"Unknown profile: {profile_name}")
455
456
def save_config(self, name):
457
"""Save current configuration."""
458
self.saved_configs[name] = dill.settings.copy()
459
print(f"Saved configuration as '{name}'")
460
461
def restore_config(self, name):
462
"""Restore saved configuration."""
463
if name in self.saved_configs:
464
dill.settings.clear()
465
dill.settings.update(self.saved_configs[name])
466
self.current_profile = None
467
print(f"Restored configuration '{name}'")
468
else:
469
print(f"No saved configuration named '{name}'")
470
471
def list_configs(self):
472
"""List saved configurations."""
473
print("Saved configurations:")
474
for name in self.saved_configs:
475
print(f" - {name}")
476
477
def show_current_config(self):
478
"""Show current configuration."""
479
print("Current configuration:")
480
for key, value in dill.settings.items():
481
print(f" {key}: {value}")
482
if self.current_profile:
483
print(f"Profile: {self.current_profile}")
484
485
# Usage example
486
config_manager = ConfigManager()
487
488
# Test different profiles
489
test_obj = {
490
'function': lambda x: x**2,
491
'data': list(range(100)),
492
'nested': {'level1': {'level2': 'deep_value'}}
493
}
494
495
profiles_to_test = ['performance', 'compact', 'compatible', 'debug']
496
497
for profile in profiles_to_test:
498
print(f"\\n{'='*50}")
499
print(f"Testing {profile.upper()} profile")
500
print('='*50)
501
502
config_manager.apply_profile(profile)
503
504
try:
505
serialized = dill.dumps(test_obj)
506
restored = dill.loads(serialized)
507
508
print(f"✓ Serialization successful")
509
print(f" Size: {len(serialized):,} bytes")
510
print(f" Function test: {restored['function'](5)}")
511
512
except Exception as e:
513
print(f"✗ Failed: {e}")
514
515
# Restore original configuration
516
config_manager.restore_config('pre_profile')
517
```
518
519
### Environment-Specific Configuration
520
521
```python
522
import dill
523
import os
524
import platform
525
import sys
526
527
class EnvironmentConfig:
528
"""Automatically configure dill based on environment."""
529
530
@staticmethod
531
def auto_configure():
532
"""Automatically configure based on environment."""
533
config = {}
534
535
# Python version considerations
536
if sys.version_info >= (3, 8):
537
config['protocol'] = dill.HIGHEST_PROTOCOL
538
else:
539
config['protocol'] = 3 # Safe for older versions
540
541
# Platform considerations
542
if platform.system() == 'Windows':
543
config['fmode'] = dill.CONTENTS_FMODE # Better Windows compatibility
544
else:
545
config['fmode'] = dill.HANDLE_FMODE
546
547
# Memory considerations
548
import psutil
549
available_memory = psutil.virtual_memory().available
550
if available_memory < 1024**3: # Less than 1GB
551
config['byref'] = True # Save memory
552
config['recurse'] = False
553
else:
554
config['byref'] = False
555
config['recurse'] = True
556
557
# Development vs production
558
if os.environ.get('DILL_DEBUG'):
559
config['protocol'] = 0 # Human-readable
560
config['ignore'] = False # Strict error handling
561
elif os.environ.get('DILL_PRODUCTION'):
562
config['ignore'] = True # Lenient error handling
563
564
# Apply configuration
565
dill.settings.update(config)
566
567
print("Auto-configured dill based on environment:")
568
for key, value in config.items():
569
print(f" {key}: {value}")
570
571
return config
572
573
@staticmethod
574
def validate_config():
575
"""Validate current configuration."""
576
issues = []
577
578
# Check protocol compatibility
579
if dill.settings['protocol'] > 4 and sys.version_info < (3, 8):
580
issues.append("Protocol 5 requires Python 3.8+")
581
582
# Check memory usage with byref setting
583
if not dill.settings['byref'] and dill.settings['recurse']:
584
issues.append("High memory usage: recurse=True with byref=False")
585
586
# Check file mode compatibility
587
if dill.settings['fmode'] == dill.HANDLE_FMODE and platform.system() == 'Windows':
588
issues.append("HANDLE_FMODE may have issues on Windows")
589
590
if issues:
591
print("Configuration issues found:")
592
for issue in issues:
593
print(f" ⚠ {issue}")
594
else:
595
print("✓ Configuration validation passed")
596
597
return len(issues) == 0
598
599
# Example usage
600
print("Current system information:")
601
print(f" Python: {sys.version}")
602
print(f" Platform: {platform.system()}")
603
print(f" Memory: {psutil.virtual_memory().total / (1024**3):.1f}GB")
604
605
# Auto-configure
606
env_config = EnvironmentConfig()
607
env_config.auto_configure()
608
609
# Validate configuration
610
env_config.validate_config()
611
612
# Test with auto-configuration
613
test_function = lambda x: x * 2 + 1
614
serialized = dill.dumps(test_function)
615
restored = dill.loads(serialized)
616
print(f"\\nTest result: {restored(10)}")
617
print(f"Serialized size: {len(serialized)} bytes")
618
```
619
620
## Best Practices
621
622
### Configuration Management Guidelines
623
624
1. **Profile Usage**: Use predefined profiles for common scenarios
625
2. **Context Managers**: Use context managers for temporary configuration changes
626
3. **Environment Awareness**: Configure based on deployment environment
627
4. **Performance Testing**: Benchmark different configurations for your use case
628
5. **Documentation**: Document configuration choices and their rationale
629
630
### Performance Considerations
631
632
1. **Protocol Selection**: Higher protocols generally offer better performance
633
2. **Memory vs Speed**: `byref=True` saves memory but may slow deserialization
634
3. **File Mode Impact**: Choose file mode based on your file handling needs
635
4. **Recursive Overhead**: Enable recursion only when necessary for complex objects