Skip to content

Instantly share code, notes, and snippets.

@cholmboe
Created October 31, 2024 11:33
Show Gist options
  • Save cholmboe/2b73068cddf55b30141cbdc53c1fb64d to your computer and use it in GitHub Desktop.
Save cholmboe/2b73068cddf55b30141cbdc53c1fb64d to your computer and use it in GitHub Desktop.
A script that wraps Python scripts to manage requirements in a virtual environment.
#!/bin/bash
# This script wraps Python scripts in a virtual environment.
# It creates a virtual environment if one doesn't exist, installs dependencies,
# and runs the Python script with the same name as this wrapper. This is a hacky
# wrapper meant for your hacky scripts, for serious stuff you should manage
# dependencies seriously.
#
# Usage:
# 1. Create a Python script named foo.py
# 2. Create a symlink to this wrapper named foo
# 3. Run ./foo with any arguments
#
# The script will:
# - Create a .venv directory if it doesn't exist
# - Install dependencies from requirements.txt if present
# - Generate requirements.txt using pipreqs if missing
# - Run the Python script, forwarding stdin and arguments
set -euo pipefail
PYTHON_SCRIPT="$0.py"
ROOT_DIR="$(dirname "$PYTHON_SCRIPT")"
# Ensure this script is not being run directly
if [ "$(basename "$0")" = "pywrap.sh" ]; then
echo "This script should not be run directly. It should be symlinked to a Python script."
exit 1
fi
# Check if the Python script exists
if [ ! -f "$PYTHON_SCRIPT" ]; then
echo "Python script $PYTHON_SCRIPT does not exist."
exit 1
fi
# Set the virtual environment's name
VENV_DIR="$ROOT_DIR/.venv"
# Check if the virtual environment already exists
if [ ! -d "$VENV_DIR" ]; then
python3 -m venv "$VENV_DIR" || { echo "Failed to create virtual environment."; exit 1; }
fi
# Activate the virtual environment
source "$VENV_DIR/bin/activate"
# Quick and dirty check if the dependencies are installed
if [ -f "$ROOT_DIR/requirements.txt" ]; then
SITE_PACKAGES=$(python -c "import site; print(site.getsitepackages()[0])")
REQUIREMENTS=$(cat "$ROOT_DIR/requirements.txt" | awk -F'==' '{print tolower($1)}')
INSTALL=$(cat <(ls "$SITE_PACKAGES") <(ls "$SITE_PACKAGES") <(cat "$ROOT_DIR/requirements.txt" | awk -F'==' '{print tolower($1)}') | sort | uniq -u)
fi
# Install the dependencies if INSTALL is set or if requirements.txt is missing
if [[ "${INSTALL:-}" != "" ]] || [ ! -f "$ROOT_DIR/requirements.txt" ]; then
command -v pipreqs &> /dev/null || pip install pipreqs &> /dev/null || { echo "Failed to install pipreqs."; exit 1; }
pipreqs "$ROOT_DIR" --ignore "$VENV_DIR" --mode no-pin --force &> /dev/null || { echo "Failed to generate requirements.txt."; exit 1; }
pip install -U -r "$ROOT_DIR/requirements.txt" &> /dev/null || { echo "Failed to install dependencies."; exit 1; }
fi
# Run the Python script with forwarded stdin
python "$PYTHON_SCRIPT" "$@" <&0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment