Skip to content

Instantly share code, notes, and snippets.

@Velocet
Last active July 6, 2024 22:29
Show Gist options
  • Save Velocet/51245270c2197023eaa9752c58fddc81 to your computer and use it in GitHub Desktop.
Save Velocet/51245270c2197023eaa9752c58fddc81 to your computer and use it in GitHub Desktop.
Snippets & Templates (python/PwSh/bash)
#!/usr/bin/env bash
# Best practices Bash script template with useful functions
# github.com/ralish/bash-script-template/blob/main/template.sh
#region INIT
if [[ ${DEBUG-} =~ ^1|yes|true$ ]]; then set -o xtrace; fi # Trace script execution if DEBUG
if ! (return 0 2> /dev/null); then
set -o errexit # Exit on Errors
set -o nounset # Disallow Unset Variables
set -o pipefail # Use last non-zero exit code in a pipeline
fi # Alternative Shell behaviours if not sourced
set -o errtrace # Ensure the error trap handler is inherited
function script_trap_err() { # Handler for unexpected errors
local exit_code=1
trap - ERR # Disable trap handler to prevent recursion
set +o errexit # Consider further errors non-fatal
set +o pipefail # to ensure script completion
if [[ ${1-} =~ ^[0-9]+$ ]]; then exit_code="$1"; fi # Check exit code
if [[ -n ${cron-} ]]; then # Print dbg data if in Cron mode
if [[ -n ${script_output-} ]]; then exec 1>&3 2>&4; fi
printf '%b\n' "$ta_none" # Print basic dbg info
printf '***** Abnormal termination of script *****\n'
printf 'Script Path: %s\n' "$script_path"
printf 'Script Parameters: %s\n' "$script_params"
printf 'Script Exit Code: %s\n' "$exit_code"
if [[ -n ${script_output-} ]]; then printf 'Script Output:\n\n%s' "$(cat "$script_output")"
else printf 'Script Output: None (failed before log init)\n'; fi
fi # Print script log
exit "$exit_code" # Exit with failure status
} # script_trap_err
function script_trap_exit() { # Handler for exiting the script
cd "$orig_cwd"
if [[ -n ${cron-} && -f ${script_output-} ]]; then rm "$script_output"; fi # Remove Cron mode script log
if [[ -d ${script_lock-} ]]; then rmdir "$script_lock"; fi # Remove script execution lock
printf '%b' "$ta_none" # Restore colours
} # script_trap_exit
# "Minimal" safe bash script
# https://gist.github.com/m-radzikowski/53e0b39e9a59a1518990e76c2bff8038
set -Eeuo pipefail
trap cleanup SIGINT SIGTERM ERR EXIT
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...]
Script_Description.
Options:
-h, --help Print this help and exit
-v, --verbose Print script debug info
-f, --flag Some flag description
-p, --param Some param description
EOF
exit
}
cleanup() { trap - SIGINT SIGTERM ERR EXIT ;; } # script cleanup here
setup_colors() {
if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
else NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''; fi
}
msg() { echo >&2 -e "${1-}"; }
die() {
local msg=$1; local code=${2-1} # default exit status 1
msg "$msg"; exit "$code"
}
parse_params() {
flag=0; param=''; # default values of variables set from params
while :; do
case "${1-}" in
-h | --help) usage ;;
-v | --verbose) set -x ;;
--no-color) NO_COLOR=1 ;;
-f | --flag) flag=1 ;; # example flag
-p | --param) # example named parameter
param="${2-}"
shift
;;
-?*) die "Unknown option: $1" ;;
*) break ;;
esac
shift
done
args=("$@")
[[ -z "${param-}" ]] && die "Missing required parameter: param"
[[ ${#args[@]} -eq 0 ]] && die "Missing script arguments"
return 0
}
parse_params "$@"; setup_colors
# script logic here
msg "${RED}Read parameters:${NOFORMAT}"
msg "- flag: ${flag}"
msg "- param: ${param}"
msg "- arguments: ${args[*]-}"
# Upgrade all outdated pip packages as a one-liner
pip list --outdated | tail -n +3 | cut -d' ' -f1 | xargs -n1 pip install --upgrade
# ... or when using pip.conf
export PIP_CONFIG_FILE=".vscode/pip.conf"
pip list --outdated | tail -n +3 | cut -d' ' -f1 | xargs -n1 pip install
#!/usr/bin/env python -m pip
#
# https://pip.pypa.io/en/stable/topics/configuration/
#
# Environment Variable: PIP_CONFIG_FILE
# Usage: export PIP_CONFIG_FILE=".vscode/pip.conf"
#
[install]
# Upgrade packages to the newest version
upgrade = true
# Fail After: 1 second
timeout = 5
# Fail After: 1 Error
retries = 1
# Don't output anything except errors
quiet = 3
# Don't ask for user input
no-input = true
# Don't output colors to avoid parsing errors
no-color = true
# Don't check python version
no-python-version-warning = true
# Don't check pip version
disable-pip-version-check = true
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*-python-*- vim:ft=py:
# /// script
# ///
#region init
# Disable black formatter
# fmt: off
# Disable pylint error message for line-too-long
# pylint: disable=line-too-long
""" One-line Short Description.
A longer description that spans multiple lines: Explain the purpose.
This DocString is shown on import: Write reasonably useful and informative!
more text...
Args:
var_name(var_type): description. Default: 'var_name value'
...
Returns:
A python object containing something.
"""
__version__ = "0.6.9"
__date__ = "2024-06-09"
__status__ = "Development"
__license__ = "MIT"
__credits__ = "© iown-homecontrol"
__copyright__ = "© iown-homecontrol"
__author__ = "Velocet"
__maintainer__ = "Velocet"
__email__ = "git@mail.velocet.de"
import argparse # Argument Parser
import pathlib # file and path handling
import sys # Python Version and standard system functionality (stdin/stdout)
# import pprint # Pretty printer for data
# pprint = pprint.PrettyPrinter(width=256).pprint # Pretty printer configuration
# print = pprint # redefine pprint as print
is_even = lambda number: number % 2 == 0
print(f"{__debug__ = }")
if __debug__:
def assert_python3():
""" Assert Python >= 3.11 is used. """
assert(sys.version_info.major == 3)
assert(sys.version_info.minor >= 11)
else:
if sys.version_info.minor < 11: raise Exception("Python >= 3.11 is required!")
""" Global Variables """
verbose:bool = False # Verbose Output
#endregion init
def main(args: Any) -> None:
""" Main Entry Point """
# def main(*args: Any, **kwargs: Any) -> None:
# kwargs["arg_name"] = arg_value
# super().main(*args, **kwargs)
global verbose
if (args.verbose): verbose = True
# verbose = (False, True)[args.verbose] # type: ignore
if not args and not sys.argv[1:]:
# exit if no arguments are given
print('[ERROR] No args/argv given!')
sys.exit(1)
#
# if args or sys.argv[1:]:
# if args.verbose:
# print('[args] Found!')
# if sys.argv[1:]:
# args = sys.argv[1:] # strip file name
# else:
# verbose:bool = (False, True)[args.verbose] # type: ignore
#
# argc = vars(args) # convert to dict. access via argc['arg_name']
# Create list with 8 elements, all containing 0.
# data:list = [0] * 8 # Always initialize your variables with a value!
# do_something(data)
# def do_something(data:list = None) -> None:
# """ It does something! """
# print("Do something with this: ", data)
# # simple print error function
# # TODO create verbose function
# def prettyprinterror(*args, **kwargs):
#
# # Version 1: print to stderr using print()
# print(*args, file=sys.stderr, **kwargs)
#
# # Version 2: print to stderr using sys.stderr.write()
# sys.stderr.write('Error Message!\n')
#
# # Version 3: print to stderr using logging
# # https://docs.python.org/3/library/logging.html
# import logging
# logging.basicConfig(format='%(message)s')
# log = logging.getLogger(__name__)
# log.warning('Error Message!')
if __name__ == "__main__":
""" Executed when run from CLI or the module is not initialized from an import statement """
#region Arguments
#
# Parser Configuration
parser = argparse.ArgumentParser(
# Program Name (default: os.path.basename(sys.argv[0]))
prog="Program-Name",
# Usage description (default: generated from args)
usage="%(prog)s --input [FILE] --output [FILE(optional)]",
# Text before argument help (default: None)
description="%(prog)s description text before argument help.",
# Text after the argument help (default: None)
epilog="Alex: It's funny how the colors of the real world only seem really real when you viddy them on the screen...",
)
#
# Parser Definition
#
# NOTE: If FileType is used and a subsequent argument fails the file is not closed!
# Better wait after the parser has run and then use the with-statement to manage the files.
# dest: Specify the attribute name used in the result namespace
# metavar: Alternate display name for the argument as shown in help
# type: Automatically convert an argument to the given type
#
# test if input exists: "type=pathlib.Path.is_file()"
parser.add_argument("-i", "--input", metavar="FILE", dest="infile", type=str, required=True, help="Input File Description")
parser.add_argument("-o", "--output", metavar="FILE", dest="outfile", type=str, required=False, help="Output File Description")
# test if dir exists: "pathlib.Path.is_dir()"
parser.add_argument("-p", "--path", metavar="PATH", dest="outdir", type=str, required=False, default="./output", help="Output Directory (default: %(default)s)")
# Optional: Verbosity
parser.add_argument("-V", "--verbose", action="store_true", dest="verbose", default=False, help="Verbose Output")
# Optional: Version output
parser.add_argument("-v","--version", action="version", version="%(prog)s v{version}".format(version=__version__))
# Parse Arguments
args = parser.parse_args()
# args = parser.parse_args(['--arg1', 'arg1_value', '--arg2']) # Test arguments with predefined values
#endregion Arguments
# terminate with defined status and message
# if (args.argument):
# parser.exit(status=0, message="Write Message and exit")
# if (args.some_argument):
# print(some_argument)
if (not pathlib.Path(args.infile).is_file()):
parser.error("--input must be a file!") # print message and exit with status = 2
# Use this if you have multiple input values
# input_items = itertools.chain.from_iterable(map(glob.iglob, args.input))
# input_files = filter(os.path.isfile, input_items)
main(args) # https://docs.python.org/3/library/__main__.html
# Re-Activate black
# fmt: on
function Update-PythonPipPackages {
<#
.SYNOPSIS
Update all outdated pip Packages
.DESCRIPTION
Upgrade every outdated pip package and take care of error handling.
Includes Verbose Output. Turn on with the -verbose switch.
.ONELINER: (pip list --outdated) | Select -Skip 2 | % {pip install --upgrade $($_.split(' ')[0])}
.LINK
https://pip.pypa.io/en/stable/user_guide/#command-completion
python -m pip completion --powershell | Out-File -Encoding default -Append $PROFILE
.NOTES
Author: Velocet
Website: https://github.com/velocet
#>
[CmdletBinding()]
BEGIN {
#region PipEnviromentVars
# https://pip.pypa.io/en/stable/topics/configuration/#environment-variables
Write-Verbose -Message '[PIP] Set pip Environment Variables.'
$env:PIP_CONFIG_FILE = 'pip.conf'
$env:PIP_DISABLE_PIP_VERSION_CHECK = $true # Don't check pip version
$env:PIP_NO_PYTHON_VERSION_WARNING = $true # Don't check python version
$env:PIP_NO_INPUT = $true # Don't ask for user input
$env:PIP_NO_COLOR = $true # Don't output colors to avoid parsing errors
$env:PIP_QUIET = 3 # Don't output anything except errors
$env:PIP_RETRIES = 1 # Fail after 1 error
$env:PIP_TIMEOUT = 1 # Fail after 1 second
Write-Verbose -Message "[PIP] Use py on Windows as it takes care of versions, etc."
$PyRun = $IsWindows ? 'py' : 'python'
$PipRun = $("$PyRun -m pip")
#endregion PipEnviromentVars
#region PipEnsurePip
Write-Verbose -Message "[PIP] Check version to ensure correct install."
Invoke-Expression -Command $("$PyRun --version") # Display python version
Invoke-Expression -Command $("$PipRun --version") # Display pip version
if ($LastExitCode) { # Check if pip version failed...
# https://docs.python.org/3/library/ensurepip.html
Write-Verbose -Message "[PIP] Bootstrap pip."
Invoke-Expression -Command $("$PyRun -m ensurepip --upgrade --default-pip")
}
Write-Verbose -Message "[PIP] Upgrade: pip, setuptools, wheel."
Invoke-Expression -Command $("$PipRun install --upgrade pip setuptools wheel")
#endregion PipEnsurePip
} PROCESS {
Write-Verbose -Message "[PIP] Outdated: Get..."
[Collections.ArrayList]$Packages = Invoke-Expression -Command $("$PipRun list --outdated")
if ($LastExitCode) { Write-Error -Message "[PIP] Outdated: Error!";exit 1 }
else {
$Packages = $Packages[2..$Packages.GetUpperBound(0)] # Remove Header
if ($Packages.Count) {
Write-Verbose -Message "[PIP] Outdated: ($Packages.Count)"
foreach ($Package in $Packages) {
$Package = $Package.split(' ')[0]
Invoke-Expression -Command $("$PipRun install --upgrade $Package")
if ($LastExitCode) { Write-Error -Message "[PIP] Failed: $Package" }
else { Write-Verbose -Message "[PIP] Upgrade: $Package" }
}
} else { Write-Verbose -Message "[PIP] Upgrade: None." }
}
} END {
Write-Verbose -Message "[PIP] Unset Environment Variables..."
$env:PIP_CONFIG_FILE = ''
$env:PIP_DISABLE_PIP_VERSION_CHECK = ''
$env:PIP_NO_PYTHON_VERSION_WARNING = ''
$env:PIP_NO_INPUT = ''
$env:PIP_NO_COLOR = ''
$env:PIP_QUIET = ''
$env:PIP_RETRIES = ''
$env:PIP_TIMEOUT = ''
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment