Skip to main content

image image image

shpyx is a simple, lightweight and typed library for running shell commands in Python.

Use shpyx.run to run a shell command in a subprocess:

>>> import shpyx
>>> shpyx.run("echo 1")
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
>>> shpyx.run("echo 1").return_code
0
>>> shpyx.run("echo 1").stdout
'1\n'
>>> shpyx.run("echo 1").stderr
''

Installation

Install with pip:

pip install shpyx

How Tos

Run a command

In string format:

>>> shpyx.run("echo 1")
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)

In list format:

>>> shpyx.run(["echo", "1"])
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)

Run a command and print live output

>>> shpyx.run("echo 1", log_output=True)
1
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)

Run a command with shell specific logic

When the argument to run is a string, an actual shell is created in the subprocess and shell logic can be used. For example, the pipe operator can be used in bash/sh:

>>> shpyx.run("seq 1 5 | grep '2'")
ShellCmdResult(cmd="seq 1 5 | grep '2'", stdout='2\n', stderr='', all_output='2\n', return_code=0)

Create a custom runner

Use a custom runner to override the execution defaults, and not have to pass them to every call.

For example, we can change the default value of log_cmd, so that all commands are logged:

>>> shpyx.run("echo 1")
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)

>>> shpyx.run("echo 1", log_cmd=True)
Running: echo 1
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)

>>> runner = shpyx.Runner(log_cmd=True)
>>> runner.run("echo 1")
Running: echo 1
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)

Propagating terminal control sequences

Note: this is only supported on Linux and macOS. On any other system (including Windows), unix_raw=True raises ShpyxOSNotSupportedError.

Some commands, like psql, might output special characters used for terminal management like cursor movement and colors. For example, the psql command is used to start an interactive shell against a Postgres DB:

shpyx.run(f"psql -h {host} -p {port} -U {user} -d {database}", log_output=True)

However, the above call will not work as well as running psql directly, due to terminal control sequences not being properly propagated. To make it work well, we need to use the script utility which will properly propagate all control sequences:

# Linux:
shpyx.run(f"script -q -c 'psql -h {host} -p {port} -U {user} -d {database}'", log_output=True)
# MacOS:
shpyx.run(f"script -q /dev/null psql -h {host} -p {port} -U {user} -d {database}", log_output=True)

shpyx provides a keyword argument that does this wrapping automatically, unix_raw:

shpyx.run(f"psql -h {host} -p {port} -U {user} -d {database}", log_output=True, unix_raw=True)

It also works with list arguments, which are shell-quoted and always run through a shell:

shpyx.run(["psql", "-h", host, "-p", port, "-U", user, "-d", database], log_output=True, unix_raw=True)

The flag is disabled by default, and should only be used for interactive commands like psql.

API Reference

The following arguments are supported by Runner:

Name Description Default
log_cmd Log the executed command. False
log_output Log the live output of the command (while it is being executed). False
verify_return_code Raise an exception if the shell return code of the command is not 0. True
verify_stderr Raise an exception if anything was written to stderr during the execution. False
use_signal_names Log the name of the signal that killed the command, if any. True
decoder_factory Factory of the incremental decoder used to decode the output. UTF-8

The following arguments are supported by run:

Name Description Default
log_cmd Log the executed command. Runner default
log_output Log the live output of the command (while it is being executed). Runner default
verify_return_code Raise an exception if the shell return code of the command is not 0. Runner default
verify_stderr Raise an exception if anything was written to stderr during the execution. Runner default
use_signal_names Log the name of the signal that killed the command, if any. Runner default
decoder_factory Factory of the incremental decoder used to decode the output. Runner default
env Environment variables to set during the execution of the command. Same as parent process
exec_dir Custom path to execute the command in (defaults to current directory). Same as parent process
unix_raw (Linux/macOS only) Use the script Unix utility to run the command. False

Implementation details

shpyx is a wrapper around the excellent subprocess module, aiming to concentrate all the different API functions (Popen/communicate/poll/wait) into a single function - shpyx.run.

The output streams (stdout and stderr) are each read by a dedicated thread, and decoded incrementally (UTF-8 by default, see decoder_factory, typed as shpyx.DecoderFactory) as the output arrives. This allows logging the live output, keeps the order of the combined output (all_output), and avoids deadlocks when a command fills one pipe while shpyx waits on the other. A KeyboardInterrupt (CTRL-C), or any other exception raised during the run, stops it promptly and kills the subprocess along with all of its descendants (e.g. every command of a shell pipeline).

shpyx supports Linux, macOS and Windows. Other systems (e.g. FreeBSD) are treated as generic POSIX systems: run works as usual, but unix_raw raises ShpyxOSNotSupportedError.

Security

The call to subprocess.Popen uses shell=True when the input to run is a string (to support shell logic like bash piping). This means that an actual system shell is being created, and the subprocess has the permissions of the main Python process.

It is therefore recommended not pass any untrusted input to shpyx.run.

For more info, see security considerations.

Relevant Python libraries:

Other 3rd-party libraries for running shell commands in Python:

Contributing

Contributions are welcome!

See CONTRIBUTING.md for details.

License

shpyx is distributed under the terms of the MIT license.

Metadata

Release files for shpyx 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shpyx 0.1.0
File Size Uploaded
shpyx-0.1.0.tar.gz 90.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for shpyx 0.1.0
File Interpreter ABI Platform
shpyx-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 104.1 kB

Release files / shpyx-0.1.0.tar.gz

Download URL shpyx-0.1.0.tar.gz
Size 90.6 kB
Tags Source
SHA-256 checksum
How to use checksums
94c77ace0be9e1653d2f9025fd4768d79abf6b36845a3636aa434a5859906ceb
BLAKE2b-256 checksum
How to use checksums
05e3bb48b97c4de8052155b91dbbe493e97909442701a67c3a4fa82d519f2a50
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / shpyx-0.1.0-py3-none-any.whl

Download URL shpyx-0.1.0-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f8ed290755d3eefc36bd644cbe3e7761751c69d44fd511374c967e4c584ea371
BLAKE2b-256 checksum
How to use checksums
45cd34d439dfc8820d3bdfc1c488b3b2fd7796fc48951aeaa11ca3dccc4b62db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

0.0.38

2 release files

0.0.37

2 release files

0.0.36

2 release files

0.0.35

2 release files

0.0.34

2 release files

0.0.33

2 release files

0.0.32

2 release files

0.0.30

2 release files

0.0.29

2 release files

0.0.28

2 release files

0.0.27

2 release files

0.0.25

2 release files

0.0.23

2 release files

0.0.22

2 release files

0.0.19

2 release files

0.0.18

2 release files

0.0.17

2 release files

0.0.16

2 release files

0.0.15

2 release files

0.0.14

2 release files

0.0.13

2 release files

0.0.12

2 release files

0.0.11

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page