or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

alternative-spawning.mdindex.mdpattern-matching.mdprocess-control.mdrepl-automation.mdssh-operations.mdutilities.md

repl-automation.mddocs/

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

```