Last active
July 16, 2026 15:49
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! /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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! /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 | |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I use vim-template to populate the templates straight into my vim instance.