Using ANSI escape colors
Specific escape codes
are of the form:
\033[0m
This one clears the previous color/font style.
ESC = lambda n: f'\033[{n}m'
ENDC = ESC(0)
BOLD = ESC(1),
DIM = ESC(2)
OBLIQUE = ESC(2)
UNDER = ESC(4)
RED = ESC(91)
GREEN = ESC(92)
BLUE = ESC(94)
YELLOW = ESC(93)
MAGENTA = ESC(95)
CYAN = ESC(96)
Using subprocess to make Bash calls
Executing bash commands with subprocess
import subprocess
def run_shell(cmdl):
try:
output = subprocess.check_output(cmdl,
stderr=subprocess.STDOUT,
shell=True, text=True)
return output
except subprocess.CalledProcessError as e:
print("ERROR -", e.output); exit(1)
Accepting from both stdin and argv
Reads from stdin if piped, else argv. Supports globs.
def maplines(type):
import sys
args = (map(str.strip, sys.stdin)
if sys.stdin.isatty() else sys.argv[1:])
yield from map(type, args)
Using argparse to define CLT API
The docstring can serve as ArgumentParser
description to avoid repetition.
from argparse import ArgumentParser
p = ArgumentParser(description=__doc__)
Unary functions can be exposed directly
in the add argument step.
p.add_argument("-print", type=lambda _: print(_))
It has two issues.
- It does not work with arities other than one
- Errors are raised as parsing errors without trace
Nullary actions can be exposed directly in the
add argument step as well, but require a work-around.
from argparse import _StoreTrueAction
p.add_argument("-action", action=type('',
(_StoreTrueAction,), {"__call__":
lambda *_: print(...)} ))
Likely not worth the confusing syntax
Subclassing the argparse Action
This enables defining inline
the API for a CLT where args expose calls
to callables of any arity.
from argparse import Action
class Cmd(Action):
def __init__(self, **_):
self._cmd = _.pop("cmd")
optstr = _.pop("option_strings")
dest = _.pop("dest")
super().__init__(optstr, dest, **_)
def __call__(self, _1, _2, vals, *_3):
self._cmd(*vals)
p = ArgumentParser()
p.register("action", "cmd", Cmd)
This setup can be used like:
p.add_argument("-help", nargs=0, action="cmd",
cmd=lambda: print(__doc__))
p.add_argument("-echo", nargs=1, action="cmd",
cmd=lambda x: print(x))
p.add_argument("-catw", nargs=2, action="cmd",
cmd=lambda x, y: print(x, y))
p.add_argument("-lines", nargs="*", action="cmd",
cmd=lambda *xs: print(*xs, sep='\n'))
p.parse_args()
Post-Processing Exception Stack Traces
import io, traceback
def get_trace(exc: Exception, lines=False) -> str:
# https://stackoverflow.com/a/76584117/
BUFFER = dict(file=(file := io.StringIO()))
traceback.print_exception(exc, **BUFFER)
tr = file.getvalue().rstrip()
return tr if not lines else tr.splitlines()