Skip to main content

Epycs is a simple way to convert shell scripts to python

Project description

Epycs

Epycs is a simple way to convert shell scripts to python. It features

  • A simple subprocess API
  • A sane behaviour of exiting by default on subprocess failures
  • A show-output-on-fail behaviour

The goal of this package is to be able to write shell-script equivalent code in python while still being terse, but adding a tons of goodness in terms of arithmetical expression, string manipulation, code reuse etc...

Say no to .sh and welcome .py with epycs, you'll thank me later.

Usage

This section is a complete, copy-pasteable cheat sheet of the public API. If you are an LLM/agent: everything you need to use epycs is below — no need to read the source or search the web.

pip install epycs   # or: uv add epycs

Run a command

cmd is a magic shell: any attribute is resolved to a program on PATH.

from epycs import cmd

cmd.ls("-l", "/tmp")          # runs `ls -l /tmp`, output goes to the terminal
cmd.git("clone", url, dest)   # args are str()-ed automatically (Path, int, ...)

By default a command prints to the terminal and returns a subprocess.CompletedProcess. To capture and parse the output instead, use out_filter (see below).

Default behaviours (the "goodness" over raw subprocess)

  • Exit on failure: a non-zero return code exits your script with that code (like set -e). Disable globally with exit_on_error = False.
  • Quiet-but-loud-on-fail: with quiet=True output is hidden, unless the command fails, in which case it is dumped to stderr.
  • text=True is the default, so captured output is str, not bytes.
import epycs.subprocess as esp
esp.verbose = True          # print every command to stderr (like `set -x`)
esp.exit_on_error = False   # don't sys.exit() on non-zero; inspect .returncode

Capture & parse output: out_filter

out_filter captures stdout and runs it through a parser. Built-in filters (pass the name as a string) or any callable(str) -> object:

from epycs import cmd

txt   = cmd.cat("f.txt",  out_filter="text")          # str (raw)
lines = cmd.ls("-1",      out_filter="text_lines")     # list[str], split on \n
nul   = cmd.find("-print0", out_filter="text_lines_0") # list[str], split on \0
data  = cmd.curl(url,     out_filter="json")           # parsed JSON (dict/list)
root  = cmd.cat("f.xml",  out_filter="xml")            # xml.etree Element
rows  = cmd.cat("f.csv",  out_filter="csv")            # list[list[str]]
dicts = cmd.cat("f.csv",  out_filter="csv_dict")       # list[dict], header = keys
last  = cmd.echo("hi",    out_filter=lambda s: s.strip())  # custom callable

Available built-in filters: text, text_lines, text_lines_0, json, xml, csv, csv_dict.

Build & reuse commands: find_program and .arg(...)

find_program(name, *aliases) resolves a program once (trying aliases in order, raising ShellProgramNotFoundError if none found) and returns a reusable ShellProgram. .arg(...) returns a new command with extra args and/or default kwargs baked in (originals are never mutated):

from epycs import cmd, find_program

ls   = find_program("ls", "/usr/bin/ls")          # first match wins
echo = find_program("echo").arg(out_filter="text")  # always capture as text
echo("hello")                                      # -> "hello\n"

git  = cmd.git.arg("-C", repo)                     # `git -C <repo> ...`
git("status", "--short", out_filter="text_lines")

cmd.echo.toto(out_filter="text")  # attribute access also adds args -> `echo toto`

Keyword options

These epycs-specific kwargs work on any call; everything else is forwarded to subprocess.run (e.g. cwd=, timeout=, input=):

kwarg effect
out_filter capture stdout and parse it (see above)
quiet=True hide output unless the command fails (then dump to stderr)
stdout_tee=True capture stdout and still print it
background=True start via Popen and return it immediately (no wait)
additional_env={"K": "v"} add/override env vars; value None removes the var
additional_pathenv={"PATH": ["/x"]} append entries to a PATH-like var
cmd.long_task(quiet=True)                       # silent unless it fails
proc = cmd.server(background=True); proc.wait()  # Popen, run concurrently
cmd.make(cwd="build", additional_env={"CC": "clang"})

ec = cmd.echo.arg("-c").env(TOTO="hi")           # `.env(**vars)` == additional_env

Extras

from epycs.subprocess import source_shell_script, python_to_subprocess
from epycs.config import load_from

source_shell_script("env.sh")   # apply a shell script's env to os.environ

# load_from looks at $NAME_CONFIG then ~/.config/<name>/<name>.{toml,json,...}
cfg = load_from("myapp")        # returns parsed config, or None if absent

@python_to_subprocess            # turn a python function into a pipeable subprocess
def worker(cmd_open): ...

Changelog

  • v2.0.0

Breaking: dropped support for Python < 3.13 (requires-python = ">=3.13").

Breaking: removed the deprecated ShellProgram methods with_arg, with_default_kw and kwarg — use arg(...) instead.

pytest is now a real dependency; test/dev environment and VSCode setup.

Typing improvements: separate KW and KWVal types, typed path helper, clarified typing throughout.

Added an LLM/agent-friendly "Usage" cheat sheet to the README.

  • v1.5.0

epycs.cmd directly accessible

ShellProgram.add supports kwargs, deprecated other methods, they will be removed in a future version.

ShellProgram.env(name=value) for setting additional environment variables

tox testing on python 3.7 to 3.13

Various CI fixes

Using UV for package management

  • v1.4.0

Improved handling of additional environment (allows any str-convertible, and providing None deletes the env var)

Added new python_to_subprocess function, which turns a local python function to a full-fledged subprocess, allowing for pure-python piping.

  • v1.3.0

Added sourcing of shell scripts

Added new out_filter=text_lines_0 that splits by NUL character

  • v1.2.0

Added epycs.config for lightweight user-defined config management

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

epycs-2.0.0.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

epycs-2.0.0-py3-none-any.whl (13.4 kB view details)

Uploaded Python 3

File details

Details for the file epycs-2.0.0.tar.gz.

File metadata

  • Download URL: epycs-2.0.0.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for epycs-2.0.0.tar.gz
Algorithm Hash digest
SHA256 d44796ee416fb6a161de7c29c189075730a89241a95a9aba4c1b1310379514dc
MD5 804ce54b31ef836913e46ab4f27dfdff
BLAKE2b-256 ed980c9fc75194886dfea860019167b76ad9a16005b908e0b6410a8431bb5f6d

See more details on using hashes here.

File details

Details for the file epycs-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: epycs-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 13.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for epycs-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a48ae12d39b4b6b2678a14ef1923f70a6177da1ca98dcbeac0c567a7744a314c
MD5 1985dd97020ef4ce81d6a79f0c3b4b87
BLAKE2b-256 abcc6156505e93dd9eff9a52e626cd440c12b69e8662232d6b7934fc4253d204

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page