0
# REPL and Shell Automation
1
2
Tools for automating read-eval-print loops and interactive shells with customizable prompts and command execution. The REPLWrapper class provides high-level automation for interactive interpreters and shells.
3
4
## Capabilities
5
6
### REPL Wrapper Class
7
8
High-level wrapper for automating interactive shells and interpreters with consistent prompt handling.
9
10
```python { .api }
11
class REPLWrapper:
12
"""
13
Wrapper for read-eval-print loops (REPLs) and interactive shells.
14
15
Provides a high-level interface for automating interactive interpreters
16
by managing prompts and executing commands reliably.
17
"""
18
19
def __init__(self, cmd_or_spawn, orig_prompt, prompt_change=None,
20
new_prompt=None, continuation_prompt=None, extra_init_cmd=""):
21
"""
22
Initialize REPL wrapper.
23
24
Parameters:
25
- cmd_or_spawn (str or spawn): Command to start REPL or existing spawn instance
26
- orig_prompt (str): Regular expression matching the original prompt
27
- prompt_change (str): Command to change the prompt (optional)
28
- new_prompt (str): New prompt pattern after change (optional)
29
- continuation_prompt (str): Pattern for continuation prompts (optional)
30
- extra_init_cmd (str): Additional initialization commands
31
"""
32
33
def run_command(self, command, timeout=-1, async_=False):
34
"""
35
Execute a command in the REPL and return output.
36
37
Parameters:
38
- command (str): Command to execute
39
- timeout (int): Timeout in seconds (-1 for default)
40
- async_ (bool): Execute asynchronously (experimental, Python 3.4+ only)
41
42
Returns:
43
str: Command output (text between command and next prompt)
44
45
Raises:
46
- TIMEOUT: If command execution times out
47
- EOF: If REPL session ends unexpectedly
48
"""
49
50
def set_prompt(self, orig_prompt, prompt_change, new_prompt):
51
"""
52
Change the REPL prompt to a new pattern.
53
54
Parameters:
55
- orig_prompt (str): Current prompt pattern
56
- prompt_change (str): Command to change prompt
57
- new_prompt (str): New prompt pattern
58
"""
59
```
60
61
### REPL Constants
62
63
```python { .api }
64
PEXPECT_PROMPT: str = '[PEXPECT_PROMPT>'
65
"""Default unique prompt used by REPLWrapper for reliable operation."""
66
67
PEXPECT_CONTINUATION_PROMPT: str = '[PEXPECT_PROMPT+'
68
"""Default continuation prompt for multi-line commands."""
69
```
70
71
### Convenience Functions
72
73
Pre-configured REPL wrappers for common interpreters and shells.
74
75
```python { .api }
76
def python(command=sys.executable):
77
"""
78
Start a Python shell and return a REPLWrapper object.
79
80
Parameters:
81
- command (str): Python executable path (default: sys.executable)
82
83
Returns:
84
REPLWrapper: Configured Python REPL wrapper
85
"""
86
87
def bash(command="bash"):
88
"""
89
Start a bash shell and return a REPLWrapper object.
90
91
Parameters:
92
- command (str): Bash executable path (default: "bash")
93
94
Returns:
95
REPLWrapper: Configured bash shell wrapper
96
"""
97
98
def zsh(command="zsh", args=("--no-rcs", "-V", "+Z")):
99
"""
100
Start a zsh shell and return a REPLWrapper object.
101
102
Parameters:
103
- command (str): Zsh executable path (default: "zsh")
104
- args (tuple): Command line arguments for zsh
105
106
Returns:
107
REPLWrapper: Configured zsh shell wrapper
108
"""
109
```
110
111
## Common REPL Patterns
112
113
### Python REPL Automation
114
115
```python
116
import pexpect
117
from pexpect.replwrap import REPLWrapper
118
119
# Start Python REPL
120
python_repl = REPLWrapper('python', '>>> ', None, None)
121
122
# Execute Python commands
123
result = python_repl.run_command('2 + 2')
124
print(f"2 + 2 = {result.strip()}")
125
126
result = python_repl.run_command('import sys; sys.version')
127
print(f"Python version: {result.strip()}")
128
129
# Multi-line command
130
code = """
131
def greet(name):
132
return f"Hello, {name}!"
133
134
greet("World")
135
"""
136
result = python_repl.run_command(code)
137
print(f"Function result: {result.strip()}")
138
```
139
140
### Bash Shell Automation
141
142
```python
143
from pexpect.replwrap import REPLWrapper
144
145
# Start bash shell with custom prompt
146
bash = REPLWrapper('bash', r'\$ ', 'PS1="{}" PS2="{}" '.format(
147
pexpect.replwrap.PEXPECT_PROMPT,
148
pexpect.replwrap.PEXPECT_CONTINUATION_PROMPT),
149
pexpect.replwrap.PEXPECT_PROMPT.strip())
150
151
# Execute shell commands
152
result = bash.run_command('echo "Hello, World!"')
153
print(f"Echo result: {result.strip()}")
154
155
result = bash.run_command('ls -la | head -5')
156
print(f"Directory listing:\n{result}")
157
158
result = bash.run_command('date')
159
print(f"Current date: {result.strip()}")
160
```
161
162
### Node.js REPL Automation
163
164
```python
165
from pexpect.replwrap import REPLWrapper
166
167
# Start Node.js REPL
168
node_repl = REPLWrapper('node', '> ', None, None)
169
170
# Execute JavaScript commands
171
result = node_repl.run_command('Math.PI')
172
print(f"Pi value: {result.strip()}")
173
174
result = node_repl.run_command('console.log("Hello from Node.js")')
175
print(f"Console output: {result.strip()}")
176
177
# Define and use a function
178
js_code = """
179
function factorial(n) {
180
return n <= 1 ? 1 : n * factorial(n - 1);
181
}
182
factorial(5)
183
"""
184
result = node_repl.run_command(js_code)
185
print(f"Factorial result: {result.strip()}")
186
```
187
188
### R Statistical Computing
189
190
```python
191
from pexpect.replwrap import REPLWrapper
192
193
# Start R REPL
194
r_repl = REPLWrapper('R --vanilla', '> ', None, None)
195
196
# Execute R commands
197
result = r_repl.run_command('x <- c(1, 2, 3, 4, 5)')
198
print(f"Vector assignment: {result.strip()}")
199
200
result = r_repl.run_command('mean(x)')
201
print(f"Mean: {result.strip()}")
202
203
result = r_repl.run_command('summary(x)')
204
print(f"Summary:\n{result}")
205
```
206
207
## Advanced REPL Usage
208
209
### Custom Prompt Management
210
211
```python
212
from pexpect.replwrap import REPLWrapper
213
import pexpect
214
215
# Start shell with complex prompt setup
216
shell = REPLWrapper(
217
'bash',
218
orig_prompt=r'\$ ',
219
prompt_change='PS1="{}" PS2="{}" '.format(
220
'[MYPROMPT>', '[MYCONT>'
221
),
222
new_prompt=r'\[MYPROMPT>\s*'
223
)
224
225
# Execute commands with custom prompt
226
result = shell.run_command('whoami')
227
print(f"Current user: {result.strip()}")
228
```
229
230
### Multi-line Command Handling
231
232
```python
233
from pexpect.replwrap import REPLWrapper
234
235
# Python REPL with multi-line support
236
python = REPLWrapper('python', '>>> ', None, None)
237
238
# Multi-line function definition
239
multiline_code = '''
240
def fibonacci(n):
241
if n <= 1:
242
return n
243
else:
244
return fibonacci(n-1) + fibonacci(n-2)
245
246
# Calculate fibonacci numbers
247
for i in range(6):
248
print(f"fib({i}) = {fibonacci(i)}")
249
'''
250
251
result = python.run_command(multiline_code)
252
print("Fibonacci sequence:")
253
print(result)
254
```
255
256
### Error Handling in REPL
257
258
```python
259
from pexpect.replwrap import REPLWrapper
260
import pexpect
261
262
python = REPLWrapper('python', '>>> ', None, None)
263
264
try:
265
# This will cause a syntax error
266
result = python.run_command('print("Hello World"')
267
print("Result:", result)
268
except pexpect.TIMEOUT:
269
print("Command timed out - possibly waiting for input")
270
# Try to recover by sending closing parenthesis
271
python.child.sendline(')')
272
python.child.expect('>>> ')
273
274
try:
275
# This will cause a runtime error
276
result = python.run_command('1 / 0')
277
print("Division result:", result)
278
except Exception as e:
279
print(f"Error occurred: {e}")
280
```
281
282
### Using Existing Spawn Instance
283
284
```python
285
import pexpect
286
from pexpect.replwrap import REPLWrapper
287
288
# Create spawn instance first
289
child = pexpect.spawn('python')
290
291
# Create REPLWrapper from existing spawn
292
python = REPLWrapper(child, '>>> ', None, None)
293
294
# Use as normal
295
result = python.run_command('print("Using existing spawn")')
296
print(result)
297
298
# Access underlying spawn if needed
299
python.child.sendline('import os')
300
python.child.expect('>>> ')
301
```
302
303
### Interactive Session with Timeout
304
305
```python
306
from pexpect.replwrap import REPLWrapper
307
import pexpect
308
309
# Start REPL with custom timeout
310
repl = REPLWrapper('python', '>>> ', None, None)
311
312
try:
313
# Long-running command with timeout
314
result = repl.run_command('import time; time.sleep(2); print("Done")',
315
timeout=5)
316
print(f"Result: {result.strip()}")
317
318
except pexpect.TIMEOUT:
319
print("Command timed out")
320
# Could interrupt or terminate here
321
```
322
323
### Database CLI Automation
324
325
```python
326
from pexpect.replwrap import REPLWrapper
327
328
# Example with MySQL client (if available)
329
try:
330
mysql = REPLWrapper('mysql -u root -p', 'mysql> ', None, None)
331
332
# Execute SQL commands
333
result = mysql.run_command('SHOW DATABASES;')
334
print("Databases:")
335
print(result)
336
337
result = mysql.run_command('SELECT NOW();')
338
print(f"Current time: {result}")
339
340
except Exception as e:
341
print(f"Database connection failed: {e}")
342
```
343
344
### REPL with Custom Initialization
345
346
```python
347
from pexpect.replwrap import REPLWrapper
348
349
# Python REPL with custom initialization
350
init_commands = """
351
import math
352
import sys
353
import os
354
print("Custom Python REPL initialized")
355
"""
356
357
python = REPLWrapper('python', '>>> ', None, None, extra_init_cmd=init_commands)
358
359
# Modules are already imported
360
result = python.run_command('math.sqrt(16)')
361
print(f"Square root: {result.strip()}")
362
363
result = python.run_command('sys.version[:10]')
364
print(f"Python version: {result.strip()}")
365
```
366
367
### Jupyter Console Automation
368
369
```python
370
from pexpect.replwrap import REPLWrapper
371
372
# Start Jupyter console (if available)
373
try:
374
jupyter = REPLWrapper('jupyter console', 'In \\[\\d+\\]: ', None, None)
375
376
# Execute Jupyter commands
377
result = jupyter.run_command('import numpy as np')
378
print("NumPy imported")
379
380
result = jupyter.run_command('np.array([1, 2, 3, 4, 5]).mean()')
381
print(f"Array mean: {result.strip()}")
382
383
except Exception as e:
384
print(f"Jupyter console not available: {e}")
385
```
386
387
## Best Practices
388
389
### Reliable Prompt Detection
390
391
```python
392
# Use specific, unique prompts when possible
393
unique_prompt = '[MYAPP_PROMPT_{}]'.format(os.getpid())
394
395
repl = REPLWrapper(
396
'myapp',
397
orig_prompt='> ',
398
prompt_change=f'set_prompt "{unique_prompt}"',
399
new_prompt=re.escape(unique_prompt)
400
)
401
```
402
403
### Resource Management
404
405
```python
406
from pexpect.replwrap import REPLWrapper
407
408
# Use try/finally for cleanup
409
repl = None
410
try:
411
repl = REPLWrapper('python', '>>> ', None, None)
412
413
# Your REPL operations here
414
result = repl.run_command('print("Hello")')
415
416
finally:
417
if repl and hasattr(repl, 'child'):
418
repl.child.close()
419
```
420
421
### Context Manager Pattern
422
423
```python
424
import contextlib
425
from pexpect.replwrap import REPLWrapper
426
427
@contextlib.contextmanager
428
def repl_session(command, prompt):
429
"""Context manager for REPL sessions."""
430
repl = REPLWrapper(command, prompt, None, None)
431
try:
432
yield repl
433
finally:
434
if hasattr(repl, 'child'):
435
repl.child.close()
436
437
# Usage
438
with repl_session('python', '>>> ') as python:
439
result = python.run_command('2 + 2')
440
print(f"Result: {result.strip()}")
441
```