Skip to content

Instantly share code, notes, and snippets.

@MstWntd
Last active July 16, 2026 15:49
Show Gist options
  • Select an option

  • Save MstWntd/646429e25d8f5fa713792e716dcd9de1 to your computer and use it in GitHub Desktop.

Select an option

Save MstWntd/646429e25d8f5fa713792e716dcd9de1 to your computer and use it in GitHub Desktop.
Python and BASH script templates, focused on debugging with pdb, logging, error capture. improvements are welcome.
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
"""
Author : {{NAME}}
Email : {{EMAIL}}
Version : 0.1
Created : {{TIMESTAMP}}
Last Modified:
Description : {{CURSOR}}
"""
import argparse
import logging
import os
import sys
import time
from logging import Formatter
logger: logging.Logger = None # type: ignore[assignment]
args: argparse.Namespace = None # type: ignore[assignment]
def exc_hndlr(etype, value, tb):
logger.critical(f"Uncaught exception: {value}", exc_info=(etype, value, tb))
if not args.stdo:
import traceback
traceback.print_exception(etype, value, tb)
if os.isatty(1) and os.isatty(2):
import pdb
print()
pdb.post_mortem(tb)
def parse_args():
def _str2bool(v):
if v.lower() in ("yes", "true", "t", "y", "1"):
return True
elif v.lower() in ("no", "false", "f", "n", "0"):
return False
else:
raise argparse.ArgumentTypeError("Boolean value expected.")
parser = argparse.ArgumentParser()
parser.add_argument(
"--debug",
nargs="?",
const=True,
type=_str2bool,
default=False,
help="Debug Mode",
)
parser.add_argument(
"--cprofile",
nargs="?",
const=True,
type=_str2bool,
default=False,
help="Run main with cProfile",
)
parser.add_argument(
"--stdo",
nargs="?",
const=True,
type=_str2bool,
default=True,
help="Enable output to console",
)
parser.add_argument(
"--pdb",
nargs="?",
const=True,
type=_str2bool,
default=False,
help="Enable PDB",
)
parser.add_argument(
"--syslog",
nargs="?",
const=True,
type=_str2bool,
default=False,
help="Enable output to syslog",
)
parser.add_argument(
"--logdir",
nargs="?",
const=1,
type=str,
default="/x/a/core/logs/",
help="set log dir, default is /x/a/core/logs/",
)
parsed, unknown = parser.parse_known_args()
if unknown:
print(
f"Warning: unrecognised arguments will be ignored: {unknown}",
file=sys.stderr,
)
return parsed, unknown
def logger_init(logname=os.path.basename(__file__)[:-3]):
if args.debug:
loglvl = logging.DEBUG
fmt = "%(asctime)s\t%(levelname)s\t%(name)s\t%(filename)s::%(funcName)s():%(lineno)-3s\t%(message)s"
else:
loglvl = logging.INFO
fmt = "%(asctime)s\t%(levelname)s\t%(message)s"
logging.Formatter.converter = time.gmtime
logging.Formatter.default_msec_format = "%s.%03d"
logdir = args.logdir if os.path.isdir(args.logdir) else "./logs/"
try:
os.makedirs(logdir, exist_ok=True)
except OSError as e:
print(
f"Warning: could not create log directory '{logdir}': {e}", file=sys.stderr
)
logdir = "."
app_log = os.path.join(logdir, f"{logname}.log")
logging.basicConfig(filename=app_log, level=loglvl, format=fmt)
global logger
logger = logging.getLogger()
if args.stdo:
console = logging.StreamHandler(sys.stdout)
console.setLevel(loglvl)
if os.isatty(1) and os.isatty(2):
from copy import copy
class ColoredFormatter(Formatter):
RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(31, 38)
MAPPING = {
"WARNING": YELLOW,
"INFO": CYAN,
"DEBUG": BLUE,
"CRITICAL": RED + 10,
"ERROR": RED,
}
PREFIX = "\033["
RESET = "\033[0m"
def __init__(self, pattern):
Formatter.__init__(self, pattern)
def format(self, record):
colored_record = copy(record)
levelname = colored_record.levelname
seq = self.MAPPING.get(levelname, self.WHITE)
colored_levelname = (
f"{self.PREFIX}1m{self.PREFIX}{seq}m{levelname}{self.RESET}"
)
colored_record.levelname = colored_levelname
return Formatter.format(self, colored_record)
cf = ColoredFormatter(fmt)
console.setFormatter(cf)
else:
console.setFormatter(Formatter(fmt))
logger.addHandler(console)
if args.pdb:
import pdb
def info(tye, value, tb):
if os.isatty(1) and os.isatty(2):
logger.info("pdb triggered..")
import traceback
traceback.print_exception(tye, value, tb)
print()
pdb.post_mortem(tb)
else:
logger.error("Cant start PDB without TTY")
if os.isatty(1) and os.isatty(2):
sys.excepthook = info
pdb.set_trace()
else:
logger.error("Cant start PDB without TTY")
if args.syslog:
from logging.handlers import SysLogHandler
syslog = SysLogHandler(address="/dev/log", facility=SysLogHandler.LOG_LOCAL5)
syslog.setLevel(loglvl)
syslog.setFormatter(logging.Formatter(fmt))
logger.addHandler(syslog)
def main():
logger.info("Hi..")
if __name__ == "__main__":
args, unknown_args = parse_args()
logger_init()
sys.excepthook = exc_hndlr
if args.cprofile:
import cProfile
cProfile.run("main()", sort="cumtime")
else:
main()
#! /usr/bin/env bash
: <<MISC
Author : {{NAME}}
Email : {{EMAIL}}
Version : 0.1
Created : {{TIMESTAMP}}
Last Modified:
Description : {{CURSOR}}
MISC
declare -A colours
colours=(
['black']='\E[0;30m'
['red']='\E[0;31m'
['error']='\E[0;31m'
['fatal']='\E[41;30m'
['green']='\E[0;32m'
['yellow']='\E[0;33m'
['debug']='\E[0;33m'
['blue']='\E[0;34m'
['info']='\E[0;34m'
['magenta']='\E[0;35m'
['cyan']='\E[0;36m'
['white']='\E[0;37m'
['reset']='\E[0;00m'
)
_log() {
local lvl="${1^^}"
local msg=$2
{ [[ "$debug" == "0" ]] && [[ "$lvl" == DEBUG* ]]; } && return 0
test -t 1 && lvl="${colours[${lvl,,}]}${lvl}${colours['reset']}"
if [[ "$lvl" == DEBUG* || "$lvl" == "ERROR" ]]; then
(>&2 echo -e "$(date -u +"%Y-%m-%d %H:%M:%S.%3N %z")\t${lvl}\t${msg}")
else
echo -e "$(date -u +"%Y-%m-%d %H:%M:%S.%3N %z")\t${lvl}\t${msg}"
fi
}
show_help() {
cat <<HELP
Usage: ${BASH_SOURCE[0]} [options]
options:
-h, --help Show this help.
-l, --log [log-file] Log output.
--test Enable test mode.
--debug [012] Enable debug logging.
HELP
}
parse_args() {
if [[ "$#" == "0" ]]; then
show_help
exit 0
fi
while [[ $# -gt 0 ]]; do
opt="$1"
shift
case "$opt" in
-h | \? | --help)
show_help
exit 0
;;
--test)
test="1"
;;
-l | --log)
exec > >(tee -a "${1-cmds.log}") 2>&1
[[ -n "${1-}" && "${1-}" != "-"* ]] && shift
;;
--debug)
debug="${1-1}"
if [[ "$debug" -gt "1" ]]; then
export PS4=" \\D{%Y-%m-%d %T %z}\\011debug$debug\\011\${BASH_SOURCE}:\${LINENO}(\${FUNCNAME[0]-root})\\011"
set -xv
fi
if [[ -n "${1-}" && "${1-}" != "-"* ]]; then
shift
fi
;;
esac
done
[[ "$test" == "1" ]] && _log "info" "Test mode enabled"
return 0
}
main() {
_log "info" "hello.."
}
catch() {
local xcode=$?
local bcmd="$BASH_COMMAND"
if [[ "$xcode" != "0" && "$bcmd" != 'return '* ]]; then
_log "error" "CMD: $(whoami)@$HOSTNAME:$0 $args"
_log "error" "PWD: $PWD"
_log "error" "BCMD: $bcmd"
_log "error" "SUDO_USER: ${SUDO_USER-n/a}"
_log "fatal" "code: $xcode"
_log "fatal" "[$(caller)] $*"
_log "fatal" "BASH_SOURCE: ${BASH_SOURCE[*]}"
_log "fatal" "BASH_LINENO: ${BASH_LINENO[*]}"
_log "fatal" "FUNCNAME: ${FUNCNAME[*]}"
fi
}
set -Euo pipefail
trap catch ERR
test=0
debug=1
xdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
declare -a args=("$@")
parse_args "${args[@]}"
main
@MstWntd

MstWntd commented May 13, 2025

Copy link
Copy Markdown
Author

I use vim-template to populate the templates straight into my vim instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment