Python¶
Xonsh is built on top of Python and Python code can be executed natively alongside shell commands. This page collects notes on how Python-specific behaviour interacts with the shell.
Python versions support policy¶
Xonsh adopts NEP-0029 in supporting Python versions. Simply speaking a minor Python release (X.*) will be supported for 42 months from its date of initial release. Since Python has adopted yearly release cycle, most of the time, the latest 4 minor versions of Python would be supported at any given time.
Importing Python modules from a local directory¶
The modules available for import in a given xonsh session depend on what’s
available in sys.path. If you want to be able to import a module that
resides in the current directory, ensure that there is an empty string as the
first element of your sys.path:
@ import sys
@ sys.path.insert(0, '')
Inline scripting¶
Inline import¶
Use @.imp as an inline importer (xonsh >= 0.18.2) — modules are looked
up lazily without a preceding import statement:
@ @.imp.json.loads($(echo '{"a":1}'))
{'a': 1}
@ @.imp.datetime.datetime.now().isoformat()
'2024-02-12T15:29:57.125696'
@ @.imp.hashlib.md5(b'Hello world').hexdigest()
'3e25960a79dbc69b674cd4ec67a72c62'
Autocompletion works too — e.g. @.imp.date<TAB>.
Inline statements¶
Use $[...] (or $(...)) to embed subprocess statements directly
inside Python expressions — loops, comprehensions, conditionals, and
context managers all compose naturally:
@ for i in range(1, 5): $[echo @(i)]
@ [$[echo @(i)] for i in range(1, 5)]
@ if $(which vim): $[echo vim]
@ $[echo vim] if $(which vim) else $[echo vi]
@ with @.env.swap(QWE=1): $[bash -c 'echo $QWE']
@ $A=1 $B=2 bash -c 'echo $A $B'
String tricks¶
Triple quotes¶
To avoid escape characters (echo "\"hello\"") and keep strings readable,
use triple quotes:
@ echo """{"hello":'world'}"""
{"hello":'world'}
f-strings in commands¶
f-strings work both inside @(...) and standalone as arguments
(xonsh >= 0.23.0):
@ echo @(f'Hello {$HOME}')
Hello /home/snail
@ echo f'Hello {$HOME}'
Hello /home/snail
Walrus operator in action¶
Python’s walrus operator := (PEP 572)
lets you capture a value inline and reuse it later.
In subprocess:
@ echo Hello @(_name := input('Name: ')) # use ``_`` to keep the env clean
@ echo Hello again @(_name)
Name: Mike
Hello Mike
Hello again Mike
Works with JSON/struct output too — decorators like @json return Python
objects which the walrus captures for reuse:
@ (servers := $(@json echo '["srv1", "srv2"]'))
['srv1', 'srv2']
@ echo @(servers[0])
srv1
Shadowing between shell commands and Python names¶
Because xonsh parses Python-mode first, a bare name that resolves as a
Python built-in is evaluated as Python rather than run as a shell
command. Classic collisions: id, zip, dir, import.
@ id
<built-in function id> # Python built-in, not /usr/bin/id
Workarounds:
Flip the experimental toggle $XONSH_BUILTINS_TO_CMD — when set, bare built-in names are run as subprocess commands if a matching alias or executable exists, falling back to the Python built-in otherwise. The same switch is also useful on Windows for
dir(see platforms.rst).Change the case:
ZiporZIP— Python names are case-sensitive, soZipmisses the built-in and falls through to a command lookup. On case-insensitive filesystems (macOS default, Windows)Zipthen resolves to thezipexecutable. On case-sensitive filesystems (typical Linux) this only works if such a name exists in$PATH— use a different workaround below.Force subprocess mode with
$[...],$(...)or a redirect:$[id],$(id).Alias it under a different name:
aliases['ids'] = 'id'.Use `xontrib-abbrevs` to auto-expand on space.
See also: xonsh-cheatsheet for more copy-pastable examples.