0
# Utilities
1
2
Utility modules and scripts that complement the core gnureadline functionality, including system integration and alternative import mechanisms.
3
4
## readline.py Module
5
6
Pure Python wrapper module that imports all gnureadline functionality for systems without a built-in readline module.
7
8
```python { .api }
9
# All gnureadline functions are available through this module
10
from gnureadline import *
11
from gnureadline import __doc__
12
```
13
14
### Usage
15
16
This module is designed for systems like ActiveState Python that have no default readline module at all.
17
18
```python
19
# For systems without any readline module
20
import readline # This will import the gnureadline wrapper
21
22
# All gnureadline functions are now available as readline functions
23
readline.add_history("test command")
24
readline.set_completer(my_completer)
25
```
26
27
**Warning**: This module should not be used to override an existing system readline. Use the `override_readline` module instead for that purpose.
28
29
## override_readline Module
30
31
Installation utility for system-wide readline replacement via Python's site customization mechanism. This script safely overrides the default readline module with gnureadline.
32
33
### Core Functions
34
35
```python { .api }
36
def main() -> int:
37
"""
38
Main entry point for override installation.
39
40
Returns:
41
int: Exit code (0 for success, 1 for failure)
42
"""
43
44
def check_module(module_name: str):
45
"""
46
Import and analyze a readline module.
47
48
Parameters:
49
- module_name: Name of module to check ('readline' or 'gnureadline')
50
51
Returns:
52
module or None: Imported module or None if import failed
53
"""
54
55
def install_override(customize_path: str):
56
"""
57
Install readline override in specified customization file.
58
59
Parameters:
60
- customize_path: Path to sitecustomize.py or usercustomize.py
61
"""
62
63
def override_usercustomize() -> bool:
64
"""
65
Install override in user site customization.
66
67
Returns:
68
bool: True if successful, False if failed
69
"""
70
71
def override_sitecustomize() -> bool:
72
"""
73
Install override in site customization.
74
75
Returns:
76
bool: True if successful, False if failed
77
"""
78
```
79
80
### Command-Line Usage
81
82
The recommended way to use the override module is as a script:
83
84
```bash
85
# Install override for current Python interpreter (tries user site first)
86
python -m override_readline
87
88
# Install in site customization (system-wide, skips user site)
89
python -s -m override_readline
90
91
# For specific Python version
92
python3.11 -m override_readline
93
```
94
95
### Usage Examples
96
97
```python
98
import override_readline
99
100
# Check what readline modules are available
101
print("Checking available readline modules:")
102
readline_mod = override_readline.check_module("readline")
103
gnureadline_mod = override_readline.check_module("gnureadline")
104
105
if gnureadline_mod:
106
print("gnureadline is available")
107
if readline_mod == gnureadline_mod:
108
print("readline is already overridden with gnureadline")
109
else:
110
print("readline is using a different implementation")
111
112
# Install override programmatically
113
try:
114
success = override_readline.override_usercustomize()
115
if success:
116
print("Override installed in user customization")
117
else:
118
success = override_readline.override_sitecustomize()
119
if success:
120
print("Override installed in site customization")
121
else:
122
print("Failed to install override")
123
except Exception as e:
124
print(f"Error installing override: {e}")
125
```
126
127
## Advanced Integration Examples
128
129
### Custom Site Customization
130
131
For advanced users who want to customize the override behavior:
132
133
```python
134
# Example custom sitecustomize.py content
135
import sys
136
137
def setup_gnureadline():
138
"""Custom gnureadline setup with additional configuration."""
139
try:
140
import gnureadline as readline
141
142
# Custom configuration
143
readline.set_history_length(2000)
144
readline.parse_and_bind("set bell-style none")
145
readline.parse_and_bind("set completion-ignore-case on")
146
147
# Load history file
148
import os
149
import atexit
150
151
history_file = os.path.expanduser("~/.python_history")
152
try:
153
readline.read_history_file(history_file)
154
except FileNotFoundError:
155
pass
156
157
# Save history on exit
158
def save_history():
159
try:
160
readline.write_history_file(history_file)
161
except OSError:
162
pass
163
164
atexit.register(save_history)
165
166
# Add interactive hook message
167
def add_override_message():
168
try:
169
old_hook = sys.__interactivehook__
170
except AttributeError:
171
return
172
173
def hook():
174
old_hook()
175
print("Using GNU readline with custom configuration")
176
177
sys.__interactivehook__ = hook
178
179
add_override_message()
180
181
except ImportError:
182
import readline
183
184
# Make readline available globally
185
sys.modules["readline"] = readline
186
187
# Set up readline override
188
setup_gnureadline()
189
```
190
191
### Environment-Specific Configuration
192
193
```python
194
# Environment-aware override setup
195
import os
196
import sys
197
198
def setup_readline_for_environment():
199
"""Set up readline based on environment."""
200
try:
201
import gnureadline as readline
202
203
# Different configs for different environments
204
if os.environ.get('DEVELOPMENT_MODE'):
205
# Development environment - more verbose
206
readline.parse_and_bind("set bell-style audible")
207
readline.set_history_length(5000)
208
209
# Enable all completions
210
readline.parse_and_bind("set completion-ignore-case on")
211
readline.parse_and_bind("set show-all-if-ambiguous on")
212
213
elif os.environ.get('PRODUCTION_MODE'):
214
# Production environment - minimal
215
readline.parse_and_bind("set bell-style none")
216
readline.set_history_length(100)
217
218
else:
219
# Default environment
220
readline.set_history_length(1000)
221
readline.parse_and_bind("set bell-style none")
222
223
# Environment-specific history files
224
env_name = os.environ.get('ENVIRONMENT', 'default')
225
history_file = os.path.expanduser(f"~/.python_history_{env_name}")
226
227
try:
228
readline.read_history_file(history_file)
229
except FileNotFoundError:
230
pass
231
232
import atexit
233
atexit.register(lambda: readline.write_history_file(history_file))
234
235
except ImportError:
236
import readline
237
238
sys.modules["readline"] = readline
239
240
setup_readline_for_environment()
241
```
242
243
### Application-Specific Override
244
245
```python
246
# Application-specific readline configuration
247
import sys
248
import os
249
250
class ApplicationReadline:
251
"""Application-specific readline wrapper."""
252
253
def __init__(self, app_name):
254
self.app_name = app_name
255
self.setup_readline()
256
257
def setup_readline(self):
258
"""Set up readline for this application."""
259
try:
260
import gnureadline as readline
261
self.readline = readline
262
263
# App-specific configuration
264
config_file = os.path.expanduser(f"~/.{self.app_name}_inputrc")
265
if os.path.exists(config_file):
266
readline.read_init_file(config_file)
267
268
# App-specific history
269
self.history_file = os.path.expanduser(f"~/.{self.app_name}_history")
270
try:
271
readline.read_history_file(self.history_file)
272
except FileNotFoundError:
273
pass
274
275
# Set up save on exit
276
import atexit
277
atexit.register(self.save_history)
278
279
# Custom completer
280
readline.set_completer(self.app_completer)
281
readline.parse_and_bind("tab: complete")
282
283
except ImportError:
284
import readline
285
self.readline = readline
286
287
def app_completer(self, text, state):
288
"""Application-specific completer."""
289
# Implement app-specific completion logic
290
app_commands = ['start', 'stop', 'status', 'config', 'help', 'quit']
291
matches = [cmd for cmd in app_commands if cmd.startswith(text)]
292
293
try:
294
return matches[state]
295
except IndexError:
296
return None
297
298
def save_history(self):
299
"""Save application history."""
300
try:
301
self.readline.write_history_file(self.history_file)
302
except (OSError, AttributeError):
303
pass
304
305
def get_readline(self):
306
"""Get the configured readline module."""
307
return self.readline
308
309
# Usage in application
310
def setup_app_readline(app_name):
311
"""Set up application-specific readline."""
312
app_readline = ApplicationReadline(app_name)
313
readline = app_readline.get_readline()
314
315
# Make it available as the readline module
316
sys.modules["readline"] = readline
317
318
return readline
319
320
# Example usage
321
# readline = setup_app_readline("myapp")
322
```
323
324
## Troubleshooting Utilities
325
326
### Diagnostic Functions
327
328
```python
329
import sys
330
331
def diagnose_readline():
332
"""Diagnose readline configuration and issues."""
333
print("=== Readline Diagnosis ===")
334
335
# Check if readline is available
336
try:
337
import readline
338
print(f"✓ readline module imported: {readline.__file__}")
339
print(f" Documentation: {readline.__doc__[:100]}...")
340
341
# Check if it's GNU readline or libedit
342
if hasattr(readline, 'backend'):
343
print(f" Backend: {readline.backend}")
344
elif "GNU" in readline.__doc__:
345
print(" Backend: GNU readline")
346
elif "libedit" in readline.__doc__:
347
print(" Backend: libedit")
348
else:
349
print(" Backend: unknown")
350
351
except ImportError:
352
print("✗ readline module not available")
353
return
354
355
# Check gnureadline availability
356
try:
357
import gnureadline
358
print(f"✓ gnureadline available: {gnureadline.__file__}")
359
except ImportError:
360
print("✗ gnureadline not available")
361
362
# Check if readline is overridden
363
try:
364
import readline
365
import gnureadline
366
if readline is gnureadline:
367
print("✓ readline is overridden with gnureadline")
368
else:
369
print("⚠ readline is not overridden")
370
except ImportError:
371
pass
372
373
# Check site customization
374
try:
375
import sitecustomize
376
print(f"✓ sitecustomize found: {sitecustomize.__file__}")
377
except ImportError:
378
print("- sitecustomize not found")
379
380
try:
381
import usercustomize
382
print(f"✓ usercustomize found: {usercustomize.__file__}")
383
except ImportError:
384
print("- usercustomize not found")
385
386
# Check history functionality
387
try:
388
readline.add_history("test")
389
if readline.get_current_history_length() > 0:
390
print("✓ History functionality working")
391
else:
392
print("⚠ History functionality not working")
393
except Exception as e:
394
print(f"✗ History error: {e}")
395
396
# Check completion functionality
397
try:
398
readline.set_completer(lambda text, state: None)
399
print("✓ Completion functionality working")
400
except Exception as e:
401
print(f"✗ Completion error: {e}")
402
403
print("=== End Diagnosis ===")
404
405
# diagnose_readline() # Uncomment to run diagnosis
406
```
407
408
## Installation Verification
409
410
```python
411
def verify_gnureadline_installation():
412
"""Verify that gnureadline is properly installed and configured."""
413
issues = []
414
415
# Check basic import
416
try:
417
import gnureadline
418
except ImportError:
419
issues.append("gnureadline cannot be imported - package not installed")
420
return issues
421
422
# Check C extension
423
try:
424
gnureadline.add_history("test")
425
gnureadline.clear_history()
426
except Exception as e:
427
issues.append(f"gnureadline C extension not working: {e}")
428
429
# Check override modules
430
try:
431
import readline as readline_module
432
import override_readline
433
except ImportError as e:
434
issues.append(f"Override modules not available: {e}")
435
436
# Check if override is recommended
437
try:
438
import readline
439
if "libedit" in readline.__doc__:
440
issues.append("System readline is libedit - consider running 'python -m override_readline'")
441
except ImportError:
442
pass
443
444
if not issues:
445
print("✓ gnureadline installation is working correctly")
446
else:
447
print("Issues found:")
448
for issue in issues:
449
print(f" ⚠ {issue}")
450
451
return issues
452
453
# verify_gnureadline_installation() # Uncomment to run verification
454
```