0
# Event Hooks
1
2
Advanced customization through event hooks that execute at specific points in the readline process, enabling custom display and input handling. These hooks provide fine-grained control over readline's behavior at key moments in the input cycle.
3
4
## Capabilities
5
6
### Startup Hook
7
8
Function executed just before readline prints the first prompt, allowing for initialization and setup before user interaction begins.
9
10
```python { .api }
11
def set_startup_hook(function=None):
12
"""
13
Set or remove the startup_hook function.
14
15
Parameters:
16
- function (callable, optional): Function called with no arguments just before
17
readline prints the first prompt. If None, removes hook.
18
19
Returns:
20
None
21
"""
22
```
23
24
**Usage Example:**
25
26
```python
27
import readline
28
import os
29
import time
30
31
def startup_initialization():
32
"""Initialize readline environment at startup"""
33
# Load custom history file
34
history_file = os.path.expanduser('~/.myapp_history')
35
try:
36
readline.read_history_file(history_file)
37
except FileNotFoundError:
38
pass
39
40
# Set up custom completion
41
readline.parse_and_bind('tab: complete')
42
43
# Display welcome message
44
print(f"Welcome! Loaded {readline.get_current_history_length()} history entries.")
45
print("Type 'help' for available commands.")
46
47
# Set startup hook
48
readline.set_startup_hook(startup_initialization)
49
50
# The hook will be called before the first input() call
51
user_input = input("Command: ")
52
```
53
54
### Pre-Input Hook
55
56
Function executed after the first prompt has been printed but before readline starts reading input characters, useful for pre-populating the command line or performing just-in-time setup.
57
58
```python { .api }
59
def set_pre_input_hook(function=None):
60
"""
61
Set or remove the pre_input_hook function.
62
63
Parameters:
64
- function (callable, optional): Function called with no arguments after the first
65
prompt has been printed and just before readline starts
66
reading input characters. If None, removes hook.
67
68
Returns:
69
None
70
71
Note: Only available when compiled with HAVE_RL_PRE_INPUT_HOOK
72
"""
73
```
74
75
**Usage Example:**
76
77
```python
78
import readline
79
import datetime
80
81
def pre_input_setup():
82
"""Set up command line before input begins"""
83
# Insert timestamp prefix
84
timestamp = datetime.datetime.now().strftime("%H:%M ")
85
readline.insert_text(f"[{timestamp}] ")
86
87
# Or insert last command for editing
88
history_length = readline.get_current_history_length()
89
if history_length > 0:
90
last_command = readline.get_history_item(history_length)
91
if last_command and last_command.startswith("repeat:"):
92
# Remove "repeat:" prefix and insert the command
93
command = last_command[7:] # Remove "repeat:" prefix
94
readline.insert_text(command)
95
96
# Set pre-input hook
97
readline.set_pre_input_hook(pre_input_setup)
98
99
# Hook executes after prompt is shown but before input starts
100
command = input("CMD> ")
101
```
102
103
### Advanced Pre-Input Patterns
104
105
```python
106
import readline
107
import os
108
109
class SmartPreInput:
110
def __init__(self):
111
self.context_aware = True
112
self.auto_suggest = True
113
114
def setup_input(self):
115
"""Intelligent pre-input setup based on context"""
116
if not self.context_aware:
117
return
118
119
# Check current directory for context clues
120
current_dir = os.getcwd()
121
files = os.listdir(current_dir)
122
123
# Auto-suggest based on directory contents
124
if 'Makefile' in files:
125
readline.insert_text("make ")
126
elif 'package.json' in files:
127
readline.insert_text("npm ")
128
elif 'requirements.txt' in files:
129
readline.insert_text("pip install -r requirements.txt")
130
elif 'setup.py' in files:
131
readline.insert_text("python setup.py ")
132
elif any(f.endswith('.py') for f in files):
133
py_files = [f for f in files if f.endswith('.py')]
134
if 'main.py' in py_files:
135
readline.insert_text("python main.py ")
136
else:
137
readline.insert_text("python ")
138
139
smart_input = SmartPreInput()
140
readline.set_pre_input_hook(smart_input.setup_input)
141
```
142
143
### Completion Display Hook
144
145
Function for customizing how completion matches are displayed to the user, allowing for enhanced completion interfaces and custom formatting.
146
147
```python { .api }
148
def set_completion_display_matches_hook(function=None):
149
"""
150
Set or remove the completion display function.
151
152
Parameters:
153
- function (callable, optional): Function called as function(substitution, matches,
154
longest_match_length) when displaying completion matches.
155
If None, removes hook.
156
157
Returns:
158
None
159
"""
160
```
161
162
**Usage Example:**
163
164
```python
165
import readline
166
167
def custom_completion_display(substitution, matches, longest_match_length):
168
"""Custom completion display with enhanced formatting"""
169
print(f"\nπ {len(matches)} completions for '{substitution}':")
170
171
# Group matches by type or category
172
files = []
173
dirs = []
174
commands = []
175
176
for match in matches:
177
if match.endswith('/'):
178
dirs.append(match)
179
elif '.' in match:
180
files.append(match)
181
else:
182
commands.append(match)
183
184
# Display grouped results
185
if dirs:
186
print(" π Directories:")
187
for d in dirs[:5]: # Show first 5
188
print(f" {d}")
189
if len(dirs) > 5:
190
print(f" ... and {len(dirs) - 5} more directories")
191
192
if files:
193
print(" π Files:")
194
for f in files[:5]:
195
print(f" {f}")
196
if len(files) > 5:
197
print(f" ... and {len(files) - 5} more files")
198
199
if commands:
200
print(" β‘ Commands:")
201
for c in commands[:5]:
202
print(f" {c}")
203
if len(commands) > 5:
204
print(f" ... and {len(commands) - 5} more commands")
205
206
print() # Extra line for readability
207
208
# Set custom display hook
209
readline.set_completion_display_matches_hook(custom_completion_display)
210
```
211
212
### Advanced Display Hook Example
213
214
```python
215
import readline
216
import os
217
import stat
218
219
def detailed_completion_display(substitution, matches, longest_match_length):
220
"""Detailed completion display with file information"""
221
print(f"\nπ Completions for '{substitution}' ({len(matches)} matches):")
222
223
# Sort matches: directories first, then files
224
dirs = [m for m in matches if m.endswith('/')]
225
files = [m for m in matches if not m.endswith('/')]
226
sorted_matches = dirs + files
227
228
# Display with details
229
for i, match in enumerate(sorted_matches[:10]): # Show first 10
230
if match.endswith('/'):
231
icon = "π"
232
details = "directory"
233
else:
234
icon = "π"
235
try:
236
# Get file size and modification time
237
stats = os.stat(match)
238
size = stats.st_size
239
mtime = stats.st_mtime
240
241
# Format size
242
if size < 1024:
243
size_str = f"{size}B"
244
elif size < 1024 * 1024:
245
size_str = f"{size // 1024}KB"
246
else:
247
size_str = f"{size // (1024 * 1024)}MB"
248
249
details = f"{size_str}"
250
251
# Check if executable
252
if os.access(match, os.X_OK):
253
icon = "β‘"
254
details += " (executable)"
255
256
except OSError:
257
details = "file"
258
259
print(f" {i+1:2d}. {icon} {match:<{longest_match_length + 2}} {details}")
260
261
if len(matches) > 10:
262
print(f" ... and {len(matches) - 10} more matches")
263
print()
264
265
readline.set_completion_display_matches_hook(detailed_completion_display)
266
```
267
268
## Hook Integration Patterns
269
270
### Complete Interactive Setup
271
272
```python
273
import readline
274
import atexit
275
import os
276
277
class InteractiveShell:
278
def __init__(self):
279
self.history_file = os.path.expanduser('~/.myshell_history')
280
self.setup_readline()
281
282
def setup_readline(self):
283
"""Complete readline setup with all hooks"""
284
# Set all hooks
285
readline.set_startup_hook(self.startup_hook)
286
readline.set_pre_input_hook(self.pre_input_hook)
287
readline.set_completion_display_matches_hook(self.display_hook)
288
289
# Configure completion
290
readline.set_completer(self.completer)
291
readline.parse_and_bind('tab: complete')
292
293
# Set up history
294
try:
295
readline.read_history_file(self.history_file)
296
except FileNotFoundError:
297
pass
298
299
atexit.register(self.cleanup)
300
301
def startup_hook(self):
302
"""Initialization at startup"""
303
print("π Interactive shell ready!")
304
readline.set_history_length(1000)
305
306
def pre_input_hook(self):
307
"""Pre-input setup"""
308
# Could insert context-aware prefixes here
309
pass
310
311
def display_hook(self, substitution, matches, longest_match_length):
312
"""Custom completion display"""
313
if len(matches) > 1:
314
print(f"\nπ‘ {len(matches)} options for '{substitution}':")
315
for i, match in enumerate(matches[:5]):
316
print(f" {i+1}. {match}")
317
if len(matches) > 5:
318
print(f" ... and {len(matches) - 5} more")
319
print()
320
321
def completer(self, text, state):
322
"""Simple file completion"""
323
import glob
324
matches = glob.glob(text + '*')
325
return matches[state] if state < len(matches) else None
326
327
def cleanup(self):
328
"""Cleanup when exiting"""
329
readline.write_history_file(self.history_file)
330
331
# Set up interactive shell
332
shell = InteractiveShell()
333
```
334
335
## Platform Availability
336
337
**Note**: The `set_pre_input_hook()` function is only available when compiled with `HAVE_RL_PRE_INPUT_HOOK`. It may not be available on all systems or readline installations.