Skip to content

Instantly share code, notes, and snippets.

@MstWntd
Last active May 13, 2025 22:33
Show Gist options
  • Save MstWntd/646429e25d8f5fa713792e716dcd9de1 to your computer and use it in GitHub Desktop.
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 : %USER%
Email : %MAIL%
Version : 0.1
Created : %FDATE%
Last Modified:
Host Machine : %HOST%
Description : %HERE%
"""
import argparse, logging, os, sys
def exc_hndlr(etype, value, tb):
logger.critical(
"Uncaught exception: {0}".format(str(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=1,
type=_str2bool,
default=False,
help='Debug Mode')
parser.add_argument(
'--cprofile',
nargs='?',
const=1,
type=_str2bool,
default=False,
help='Run main with cProfile')
parser.add_argument(
'--stdo',
nargs='?',
const=1,
type=bool,
default=True,
help='Enable output to console')
parser.add_argument(
'--pdb',
nargs='?',
const=1,
type=bool,
default=False,
help='Enable PDB')
parser.add_argument(
'--syslog',
nargs='?',
const=1,
type=bool,
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/')
return parser.parse_known_args()
def logger_init(logname=os.path.basename(__file__)[:-3]):
from logging import Formatter
import time
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 = "./logs/" if not os.path.isdir(args.logdir) else args.logdir
app_log = "%s/%s.log" % (logdir, logname)
if not os.path.exists(logdir):
os.makedirs(logdir)
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 = ('{0}1m{0}{1}m{2}{3}') \
.format(self.PREFIX, seq, 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()
#! /bin/bash
: <<MISC
Author : %USER%
Email : %MAIL%
Version : 0.1
Created : %FDATE%
Last Modified:
Host Machine : %HOST%
Description : %HERE%
MISC
declare -A colours;
colours=(\
['black']='\E[0;47m'\
['red']='\E[0;31m'\
['error']='\E[0;31m'\
['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
(test "$debug" == "0" && test "$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.
--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"
;;
--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]})\\011"
set -xv
fi
if [[ ! -z "$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" "SUDO_USER: $SUDO_USER"
_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 -E
trap catch ERR
test=0
debug=1
xdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
args=$@
parse_args $args
main
@MstWntd
Copy link
Author

MstWntd commented May 13, 2025

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