0
# Scripting
1
2
## Overview
3
4
Xonsh provides seamless integration between Python and shell scripting, allowing execution of xonsh code (Python + shell syntax), subprocess management, and pipeline creation. The Execer class handles code parsing and execution, while the API modules provide clean interfaces for subprocess operations.
5
6
## Code Execution Engine
7
8
### Execer Class
9
```python { .api }
10
from xonsh.execer import Execer
11
12
class Execer:
13
"""Executes xonsh code in a context-aware manner."""
14
15
def __init__(self, filename: str = "<xonsh-code>",
16
debug_level: int = 0,
17
parser_args: dict = None,
18
scriptcache: bool = True,
19
cacheall: bool = False):
20
"""Initialize code executer.
21
22
Parameters
23
----------
24
filename : str, default "<xonsh-code>"
25
Name for executed code (error reporting)
26
debug_level : int, default 0
27
Debug level for parsing (0-2)
28
parser_args : dict, optional
29
Additional parser configuration
30
scriptcache : bool, default True
31
Whether to cache compiled bytecode
32
cacheall : bool, default False
33
Whether to cache all code including interactive input
34
"""
35
36
def parse(self, input: str, ctx: dict, mode: str = "exec",
37
filename: str = None, transform: bool = True) -> object:
38
"""Parse xonsh code with context awareness.
39
40
Parameters
41
----------
42
input : str
43
Code string to parse
44
ctx : dict
45
Execution context/namespace
46
mode : str, default "exec"
47
Parsing mode ("exec", "eval", "single")
48
filename : str, optional
49
Filename for error reporting
50
transform : bool, default True
51
Whether to apply context-aware transformations
52
53
Returns
54
-------
55
object
56
Parsed AST or None if parsing failed
57
"""
58
59
def eval(self, input: str, ctx: dict = None, filename: str = None) -> object:
60
"""Evaluate xonsh expression and return result.
61
62
Parameters
63
----------
64
input : str
65
Expression to evaluate
66
ctx : dict, optional
67
Execution context
68
filename : str, optional
69
Filename for error reporting
70
71
Returns
72
-------
73
object
74
Evaluation result
75
"""
76
77
def exec(self, input: str, ctx: dict = None, filename: str = None) -> None:
78
"""Execute xonsh code statement(s).
79
80
Parameters
81
----------
82
input : str
83
Code to execute
84
ctx : dict, optional
85
Execution context
86
filename : str, optional
87
Filename for error reporting
88
"""
89
90
# Usage examples
91
execer = Execer()
92
93
# Execute shell commands
94
execer.exec("ls -la")
95
execer.exec("echo 'Hello World'")
96
97
# Execute Python code
98
execer.exec("x = 10")
99
result = execer.eval("x * 2") # Returns 20
100
101
# Mixed Python and shell
102
execer.exec("""
103
import os
104
files = $(ls).strip().split('\n')
105
for f in files:
106
print(f"File: {f}")
107
""")
108
```
109
110
### Context-Aware Execution
111
```python { .api }
112
from xonsh.built_ins import XSH
113
114
# Execute with custom context
115
context = {
116
'my_var': 'hello',
117
'my_func': lambda x: x.upper()
118
}
119
120
# Code that uses the context
121
code = """
122
result = my_func(my_var)
123
echo @(result) # Outputs: HELLO
124
"""
125
126
XSH.execer.exec(code, ctx=context)
127
128
# Access results from context
129
final_context = context.copy()
130
XSH.execer.exec("output = $(date)", ctx=final_context)
131
date_output = final_context['output']
132
```
133
134
## Subprocess API
135
136
### High-Level Subprocess Functions
137
```python { .api }
138
from xonsh.api.subprocess import run, check_call, check_output
139
140
def run(cmd: str|list[str], cwd: str = None, check: bool = False) -> object:
141
"""Execute subprocess with xonsh syntax.
142
143
Parameters
144
----------
145
cmd : str or list[str]
146
Command to execute
147
cwd : str, optional
148
Working directory for command
149
check : bool, default False
150
Whether to raise exception on non-zero exit
151
152
Returns
153
-------
154
object
155
Process object with returncode, stdout, stderr
156
"""
157
158
def check_call(cmd: str|list[str], cwd: str = None) -> int:
159
"""Execute subprocess, raise exception on failure.
160
161
Parameters
162
----------
163
cmd : str or list[str]
164
Command to execute
165
cwd : str, optional
166
Working directory
167
168
Returns
169
-------
170
int
171
Return code (0 on success)
172
173
Raises
174
------
175
XonshCalledProcessError
176
If command returns non-zero exit code
177
"""
178
179
def check_output(cmd: str|list[str], cwd: str = None) -> bytes:
180
"""Execute subprocess and return stdout.
181
182
Parameters
183
----------
184
cmd : str or list[str]
185
Command to execute
186
cwd : str, optional
187
Working directory
188
189
Returns
190
-------
191
bytes
192
Captured stdout content
193
194
Raises
195
------
196
XonshCalledProcessError
197
If command returns non-zero exit code
198
"""
199
200
# Usage examples
201
import tempfile
202
203
# Basic subprocess execution
204
result = run(['ls', '-la'])
205
print(f"Exit code: {result.returncode}")
206
207
# Execute in specific directory
208
with tempfile.TemporaryDirectory() as tmpdir:
209
result = run('touch test.txt', cwd=tmpdir)
210
211
# Check command success
212
try:
213
check_call(['git', 'status'])
214
print("Git repository detected")
215
except Exception:
216
print("Not a git repository")
217
218
# Capture command output
219
try:
220
output = check_output(['git', 'branch', '--show-current'])
221
branch = output.decode().strip()
222
print(f"Current branch: {branch}")
223
except Exception:
224
print("Could not get git branch")
225
```
226
227
### Low-Level Subprocess Control
228
```python { .api }
229
from xonsh.procs.specs import SubprocSpec
230
from xonsh.procs.pipelines import run_subproc
231
232
def create_subprocess_spec(cmd: list[str], **kwargs) -> SubprocSpec:
233
"""Create subprocess specification.
234
235
Parameters
236
----------
237
cmd : list[str]
238
Command and arguments
239
**kwargs
240
Additional subprocess options
241
242
Returns
243
-------
244
SubprocSpec
245
Subprocess specification object
246
"""
247
248
# Create and run subprocess
249
spec = SubprocSpec(['ls', '-la'],
250
captured='stdout',
251
stack=None)
252
result = run_subproc(spec)
253
```
254
255
## Pipeline Management
256
257
### Pipeline Creation and Execution
258
```python { .api }
259
from xonsh.procs.pipelines import Pipeline
260
261
class Pipeline:
262
"""Manages subprocess pipelines."""
263
264
def __init__(self, specs: list[SubprocSpec]):
265
"""Initialize pipeline.
266
267
Parameters
268
----------
269
specs : list[SubprocSpec]
270
List of subprocess specifications
271
"""
272
273
def run(self) -> object:
274
"""Execute the pipeline.
275
276
Returns
277
-------
278
object
279
Pipeline execution result
280
"""
281
282
# Pipeline examples using built-in functions
283
from xonsh.built_ins import subproc_captured_stdout
284
285
# Simple pipeline (command | command)
286
output = subproc_captured_stdout(['ls', '-la'])
287
lines = output.strip().split('\n')
288
python_files = [line for line in lines if '.py' in line]
289
290
# Multi-stage pipeline simulation
291
def pipeline_ls_grep_wc():
292
"""Simulate: ls | grep .py | wc -l"""
293
files = subproc_captured_stdout(['ls']).strip().split('\n')
294
py_files = [f for f in files if '.py' in f]
295
return len(py_files)
296
297
count = pipeline_ls_grep_wc()
298
```
299
300
### Process Communication
301
```python { .api }
302
from xonsh.procs.proxies import ProcProxy
303
304
# Process proxy for advanced control
305
def create_interactive_process(cmd: list[str]) -> ProcProxy:
306
"""Create interactive process proxy.
307
308
Parameters
309
----------
310
cmd : list[str]
311
Command to execute
312
313
Returns
314
-------
315
ProcProxy
316
Process proxy for interaction
317
"""
318
from xonsh.procs.specs import SubprocSpec
319
spec = SubprocSpec(cmd, captured=False)
320
return ProcProxy(spec)
321
322
# Usage with interactive commands
323
# proc = create_interactive_process(['python', '-i'])
324
# proc.communicate(input=b"print('hello')\n")
325
```
326
327
## Script Execution
328
329
### File-Based Script Execution
330
```python { .api }
331
from xonsh.codecache import run_script_with_cache, run_code_with_cache
332
333
def run_script_with_cache(filename: str, glb: dict = None,
334
loc: dict = None) -> None:
335
"""Execute xonsh script file with caching.
336
337
Parameters
338
----------
339
filename : str
340
Path to xonsh script file
341
glb : dict, optional
342
Global namespace
343
loc : dict, optional
344
Local namespace
345
"""
346
347
def run_code_with_cache(code: str, filename: str = "<code>",
348
glb: dict = None, loc: dict = None) -> None:
349
"""Execute xonsh code with bytecode caching.
350
351
Parameters
352
----------
353
code : str
354
Code string to execute
355
filename : str, default "<code>"
356
Filename for caching and error reporting
357
glb : dict, optional
358
Global namespace
359
loc : dict, optional
360
Local namespace
361
"""
362
363
# Execute script files
364
run_script_with_cache('my_script.xsh')
365
run_script_with_cache('/path/to/script.py', glb=globals())
366
367
# Execute code with caching
368
script_code = """
369
echo "Starting process..."
370
files = $(ls *.py)
371
echo f"Found {len(files.split())} Python files"
372
"""
373
run_code_with_cache(script_code, filename="list_files.xsh")
374
```
375
376
### Dynamic Code Generation
377
```python { .api }
378
from xonsh.built_ins import XSH
379
380
def generate_and_execute_script(template: str, **kwargs) -> object:
381
"""Generate and execute xonsh script from template.
382
383
Parameters
384
----------
385
template : str
386
Script template with {variable} placeholders
387
**kwargs
388
Variables to substitute in template
389
390
Returns
391
-------
392
object
393
Execution result
394
"""
395
script = template.format(**kwargs)
396
return XSH.execer.eval(script)
397
398
# Template-based script generation
399
file_template = """
400
import os
401
target_dir = "{directory}"
402
if os.path.exists(target_dir):
403
files = $(ls {directory})
404
echo f"Directory {target_dir} contains: {{files}}"
405
else:
406
echo f"Directory {target_dir} does not exist"
407
"""
408
409
result = generate_and_execute_script(file_template, directory="/tmp")
410
```
411
412
## Advanced Scripting Features
413
414
### Error Handling in Scripts
415
```python { .api }
416
from xonsh.tools import XonshError, XonshCalledProcessError
417
418
def safe_execute(cmd: str) -> tuple[bool, str]:
419
"""Safely execute command with error handling.
420
421
Parameters
422
----------
423
cmd : str
424
Command to execute
425
426
Returns
427
-------
428
tuple[bool, str]
429
(success, output_or_error)
430
"""
431
try:
432
result = XSH.execer.eval(f"$({cmd})")
433
return True, result
434
except XonshCalledProcessError as e:
435
return False, f"Command failed: {e}"
436
except XonshError as e:
437
return False, f"Xonsh error: {e}"
438
except Exception as e:
439
return False, f"Unexpected error: {e}"
440
441
# Usage
442
success, output = safe_execute("git status")
443
if success:
444
print(f"Git status: {output}")
445
else:
446
print(f"Error: {output}")
447
```
448
449
### Environment-Aware Scripting
450
```python { .api }
451
from xonsh.built_ins import XSH
452
453
def conditional_execution():
454
"""Execute commands based on environment."""
455
env = XSH.env
456
457
# Platform-specific execution
458
if env.get('ON_WINDOWS'):
459
XSH.execer.exec('dir')
460
else:
461
XSH.execer.exec('ls -la')
462
463
# Conditional tool usage
464
if 'VIRTUAL_ENV' in env:
465
print("Running in virtual environment")
466
XSH.execer.exec('pip list')
467
else:
468
print("No virtual environment active")
469
470
# Configuration-based scripting
471
def load_config_and_execute():
472
"""Load configuration and execute based on settings."""
473
env = XSH.env
474
475
# Set defaults
476
env.setdefault('PROJECT_ROOT', '.')
477
env.setdefault('BUILD_TYPE', 'debug')
478
479
# Execute based on configuration
480
build_cmd = f"make {env['BUILD_TYPE']}"
481
XSH.execer.exec(f"cd {env['PROJECT_ROOT']} && {build_cmd}")
482
```
483
484
### Macro Integration
485
```python { .api }
486
from xonsh.built_ins import call_macro
487
488
def script_with_macros():
489
"""Integrate macros in scripting."""
490
491
# Define macro (would typically be in xonshrc)
492
macro_code = """
493
def backup_files(pattern):
494
files = $(find . -name @(pattern))
495
for f in files.split():
496
cp @(f) @(f + '.bak')
497
return f"Backed up {len(files.split())} files"
498
"""
499
500
XSH.execer.exec(macro_code)
501
502
# Use macro in script
503
result = call_macro('backup_files', '*.py')
504
print(result)
505
```
506
507
The scripting API provides powerful tools for automating tasks, integrating shell commands with Python logic, and creating sophisticated command-line applications that leverage both Python's capabilities and shell command efficiency.