Files
dotfiles/zsh/functions/python-templates.zsh
2025-12-24 22:47:27 -05:00

489 lines
12 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ============================================================================
# Python Project Template Functions
# ============================================================================
# Quick project scaffolding with virtual environments
#
# Usage:
# py-new <project_name> # Create new Python project
# py-django <project_name> # Create Django project
# py-flask <project_name> # Create Flask project
# py-fastapi <project_name> # Create FastAPI project
# py-data <project_name> # Create data science project
# py-cli <project_name> # Create CLI tool project
# ============================================================================
# Source shared colors (with fallback)
source "${0:A:h}/../lib/colors.zsh" 2>/dev/null || \
source "$HOME/.dotfiles/zsh/lib/colors.zsh" 2>/dev/null || {
typeset -g DF_GREEN=$'\033[0;32m' DF_BLUE=$'\033[0;34m'
typeset -g DF_YELLOW=$'\033[1;33m' DF_CYAN=$'\033[0;36m'
typeset -g DF_RED=$'\033[0;31m' DF_NC=$'\033[0m'
}
# ============================================================================
# Configuration
# ============================================================================
typeset -g PY_TEMPLATE_BASE_DIR="${PY_TEMPLATE_BASE_DIR:-$HOME/projects}"
typeset -g PY_TEMPLATE_PYTHON="${PY_TEMPLATE_PYTHON:-python3}"
typeset -g PY_TEMPLATE_VENV_NAME="${PY_TEMPLATE_VENV_NAME:-venv}"
typeset -g PY_TEMPLATE_USE_POETRY="${PY_TEMPLATE_USE_POETRY:-false}"
typeset -g PY_TEMPLATE_GIT_INIT="${PY_TEMPLATE_GIT_INIT:-true}"
# ============================================================================
# Helper Functions
# ============================================================================
_py_print_step() {
echo -e "${DF_BLUE}==>${DF_NC} $1"
}
_py_print_success() {
echo -e "${DF_GREEN}${DF_NC} $1"
}
_py_print_info() {
echo -e "${DF_CYAN}${DF_NC} $1"
}
_py_check_project_name() {
local name="$1"
if [[ -z "$name" ]]; then
echo -e "${DF_YELLOW}${DF_NC} Project name required"
return 1
fi
if [[ -d "$name" ]]; then
echo -e "${DF_YELLOW}${DF_NC} Directory '$name' already exists"
return 1
fi
return 0
}
_py_create_venv() {
local project_dir="$1"
_py_print_step "Creating virtual environment"
if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]] && command -v poetry &>/dev/null; then
cd "$project_dir"
poetry init --no-interaction
poetry env use "$PY_TEMPLATE_PYTHON"
_py_print_success "Poetry environment created"
else
"$PY_TEMPLATE_PYTHON" -m venv "$project_dir/$PY_TEMPLATE_VENV_NAME"
_py_print_success "Virtual environment created: $PY_TEMPLATE_VENV_NAME"
fi
}
_py_create_gitignore() {
local project_dir="$1"
cat > "$project_dir/.gitignore" << 'EOF'
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
dist/
*.egg-info/
venv/
env/
.venv
.vscode/
.idea/
*.swp
.pytest_cache/
.coverage
htmlcov/
.env
*.log
*.db
*.sqlite3
.mypy_cache/
EOF
_py_print_success "Created .gitignore"
}
_py_init_git() {
local project_dir="$1"
if [[ "$PY_TEMPLATE_GIT_INIT" == "true" ]]; then
cd "$project_dir"
git init
git add .
git commit -m "Initial commit: project scaffolding"
_py_print_success "Git repository initialized"
fi
}
_py_show_next_steps() {
local project_name="$1"
local has_venv="$2"
echo
echo -e "${DF_CYAN}Next steps:${DF_NC}"
echo " cd $project_name"
if [[ "$has_venv" == "true" ]]; then
if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then
echo " poetry shell"
else
echo " source $PY_TEMPLATE_VENV_NAME/bin/activate"
fi
fi
echo " # Start coding!"
echo
}
# ============================================================================
# Base Python Project Template
# ============================================================================
py-new() {
local project_name="$1"
_py_check_project_name "$project_name" || return 1
df_print_func_name "Python Project: $project_name"
_py_print_step "Creating project structure"
mkdir -p "$project_name"/{src,tests,docs}
touch "$project_name/src/__init__.py"
touch "$project_name/tests/__init__.py"
cat > "$project_name/src/main.py" << 'EOF'
#!/usr/bin/env python3
"""Main module."""
def main():
print("Hello from Python!")
if __name__ == "__main__":
main()
EOF
cat > "$project_name/requirements.txt" << 'EOF'
# Production dependencies
# Development: pytest, black, flake8, mypy
EOF
_py_print_success "Project structure created"
_py_create_venv "$project_name"
_py_create_gitignore "$project_name"
_py_init_git "$project_name"
echo
_py_print_success "Project '$project_name' created successfully!"
_py_show_next_steps "$project_name" "true"
}
# ============================================================================
# Django Project Template
# ============================================================================
py-django() {
local project_name="$1"
_py_check_project_name "$project_name" || return 1
df_print_func_name "Django Project: $project_name"
mkdir -p "$project_name"
_py_create_venv "$project_name"
_py_print_step "Installing Django"
cd "$project_name"
"$PY_TEMPLATE_VENV_NAME/bin/pip" install django
_py_print_step "Creating Django project structure"
"$PY_TEMPLATE_VENV_NAME/bin/django-admin" startproject config .
cat > "requirements.txt" << 'EOF'
Django>=4.2.0
python-decouple>=3.8
EOF
mkdir -p apps static templates media
_py_create_gitignore "."
_py_init_git "."
cd ..
echo
_py_print_success "Django project '$project_name' created!"
_py_show_next_steps "$project_name" "true"
}
# ============================================================================
# Flask Project Template
# ============================================================================
py-flask() {
local project_name="$1"
_py_check_project_name "$project_name" || return 1
df_print_func_name "Flask Project: $project_name"
mkdir -p "$project_name"/{app/{templates,static/{css,js}},tests}
_py_create_venv "$project_name"
cd "$project_name"
_py_print_step "Installing Flask"
"$PY_TEMPLATE_VENV_NAME/bin/pip" install flask
cat > "app/__init__.py" << 'EOF'
from flask import Flask
def create_app(config=None):
app = Flask(__name__)
if config:
app.config.from_object(config)
from app.routes import main
app.register_blueprint(main)
return app
EOF
cat > "app/routes.py" << 'EOF'
from flask import Blueprint, render_template
main = Blueprint('main', __name__)
@main.route('/')
def index():
return render_template('index.html')
EOF
cat > "app.py" << 'EOF'
#!/usr/bin/env python3
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
EOF
chmod +x app.py
cat > "app/templates/index.html" << 'EOF'
<!DOCTYPE html>
<html><head><title>Flask App</title></head>
<body><h1>Welcome to Flask!</h1></body></html>
EOF
cat > "requirements.txt" << 'EOF'
Flask>=3.0.0
python-decouple>=3.8
EOF
_py_create_gitignore "."
_py_init_git "."
cd ..
echo
_py_print_success "Flask project '$project_name' created!"
_py_show_next_steps "$project_name" "true"
}
# ============================================================================
# FastAPI Project Template
# ============================================================================
py-fastapi() {
local project_name="$1"
_py_check_project_name "$project_name" || return 1
df_print_func_name "FastAPI Project: $project_name"
mkdir -p "$project_name"/{app/{api,models,schemas},tests}
_py_create_venv "$project_name"
cd "$project_name"
_py_print_step "Installing FastAPI"
"$PY_TEMPLATE_VENV_NAME/bin/pip" install fastapi uvicorn[standard] pydantic
touch "app/__init__.py"
cat > "app/main.py" << 'EOF'
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(title="My API", version="0.1.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def root():
return {"message": "Welcome to FastAPI"}
@app.get("/health")
def health():
return {"status": "healthy"}
EOF
cat > "run.py" << 'EOF'
#!/usr/bin/env python3
import uvicorn
if __name__ == "__main__":
uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)
EOF
chmod +x run.py
cat > "requirements.txt" << 'EOF'
fastapi>=0.104.0
uvicorn[standard]>=0.24.0
pydantic>=2.5.0
EOF
_py_create_gitignore "."
_py_init_git "."
cd ..
echo
_py_print_success "FastAPI project '$project_name' created!"
_py_print_info "Docs at: http://localhost:8000/docs"
_py_show_next_steps "$project_name" "true"
}
# ============================================================================
# Data Science Project Template
# ============================================================================
py-data() {
local project_name="$1"
_py_check_project_name "$project_name" || return 1
df_print_func_name "Data Science Project: $project_name"
mkdir -p "$project_name"/{data/{raw,processed},notebooks,src,models,reports/figures}
_py_create_venv "$project_name"
cd "$project_name"
_py_print_step "Installing data science packages"
"$PY_TEMPLATE_VENV_NAME/bin/pip" install pandas numpy matplotlib seaborn jupyter
touch "src/__init__.py"
touch data/raw/.gitkeep data/processed/.gitkeep
cat > "requirements.txt" << 'EOF'
pandas>=2.1.0
numpy>=1.24.0
matplotlib>=3.8.0
seaborn>=0.13.0
jupyter>=1.0.0
scikit-learn>=1.3.0
EOF
_py_create_gitignore "."
cat >> ".gitignore" << 'EOF'
*.pkl
*.h5
*.parquet
data/raw/*
data/processed/*
!data/raw/.gitkeep
!data/processed/.gitkeep
models/*.pkl
.ipynb_checkpoints
EOF
_py_init_git "."
cd ..
echo
_py_print_success "Data science project '$project_name' created!"
_py_print_info "Start Jupyter: jupyter notebook"
_py_show_next_steps "$project_name" "true"
}
# ============================================================================
# CLI Tool Project Template
# ============================================================================
py-cli() {
local project_name="$1"
_py_check_project_name "$project_name" || return 1
df_print_func_name "CLI Tool Project: $project_name"
mkdir -p "$project_name"/{src/$project_name,tests}
_py_create_venv "$project_name"
cd "$project_name"
_py_print_step "Installing click"
"$PY_TEMPLATE_VENV_NAME/bin/pip" install click
cat > "src/$project_name/__init__.py" << 'EOF'
__version__ = "0.1.0"
EOF
cat > "src/$project_name/cli.py" << 'EOF'
#!/usr/bin/env python3
import click
@click.group()
@click.version_option()
def cli():
"""CLI tool - A command-line utility."""
pass
@cli.command()
@click.argument('name', default='World')
def greet(name):
"""Greet someone."""
click.echo(f"Hello, {name}!")
if __name__ == '__main__':
cli()
EOF
cat > "setup.py" << EOF
from setuptools import setup, find_packages
setup(
name="$project_name",
version="0.1.0",
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=["click>=8.0.0"],
entry_points={"console_scripts": ["$project_name=$project_name.cli:cli"]},
)
EOF
cat > "requirements.txt" << 'EOF'
click>=8.1.0
EOF
_py_create_gitignore "."
_py_init_git "."
cd ..
echo
_py_print_success "CLI tool project '$project_name' created!"
_py_print_info "Install with: pip install -e $project_name"
_py_show_next_steps "$project_name" "true"
}
# ============================================================================
# Aliases
# ============================================================================
alias pynew='py-new'
alias pydjango='py-django'
alias pyflask='py-flask'
alias pyfast='py-fastapi'
alias pydata='py-data'
alias pycli='py-cli'
# Quick venv activation
venv() {
if [[ -d "venv" ]]; then
source venv/bin/activate
elif [[ -d ".venv" ]]; then
source .venv/bin/activate
elif [[ -d "env" ]]; then
source env/bin/activate
else
echo "No virtual environment found (venv, .venv, or env)"
return 1
fi
}