Skip to content

Instantly share code, notes, and snippets.

@ngenieer
Created October 30, 2025 06:52
Show Gist options
  • Select an option

  • Save ngenieer/c4a60f92e325ac5a41eb4030ce3b096f to your computer and use it in GitHub Desktop.

Select an option

Save ngenieer/c4a60f92e325ac5a41eb4030ce3b096f to your computer and use it in GitHub Desktop.
Manage uvx-based Python aliases in your shell rc (bash/zsh)
#!/usr/bin/env bash
set -euo pipefail
# setup_uvx_alias.sh
# Manage uvx-based Python aliases in your shell rc (bash/zsh).
#
# Usage:
# ./setup_uvx_alias.sh # install with default Python 3.12
# ./setup_uvx_alias.sh --python 3.11 # install with a different default Python
# ./setup_uvx_alias.sh --rcfile ~/.zshrc # force specific rc file
# ./setup_uvx_alias.sh --uninstall # remove the aliases block
#
# Notes:
# - Per-project version override: create a file named ".python-version" with a version (e.g., 3.11)
# in any project directory; the aliases will use that version while in that directory.
# - Safe: the script adds a clearly marked block; uninstall simply removes that block.
#
# Requirements:
# - uv must be installed and on PATH (https://github.com/astral-sh/uv).
MARK_BEGIN="# >>> UVX ALIASES (managed) >>>"
MARK_END="# <<< UVX ALIASES (managed) <<<"
DEFAULT_PY="3.12"
RCFILE=""
UNINSTALL="0"
err() { printf "\033[31m%s\033[0m\n" "$*" 1>&2; }
ok() { printf "\033[32m%s\033[0m\n" "$*"; }
info(){ printf "\033[36m%s\033[0m\n" "$*"; }
detect_shell_rc() {
if [[ -n "${RCFILE}" ]]; then
echo "$RCFILE"
return
fi
# Prefer user's current shell; fall back to bash
case "${SHELL:-}" in
*zsh) echo "${HOME}/.zshrc" ;;
*bash) echo "${HOME}/.bashrc" ;;
*)
# If unknown, choose .bashrc by default
echo "${HOME}/.bashrc"
;;
esac
}
has_uv() {
if command -v uv >/dev/null 2>&1; then
return 0
else
return 1
fi
}
backup_file() {
local file="$1"
if [[ -f "$file" ]]; then
local ts
ts="$(date +%Y%m%d-%H%M%S)"
cp "$file" "${file}.bak-${ts}"
info "Backup created: ${file}.bak-${ts}"
fi
}
remove_block() {
local file="$1"
if [[ ! -f "$file" ]]; then
info "RC file not found, nothing to remove: $file"
return 0
fi
if grep -Fq "$MARK_BEGIN" "$file"; then
# Use awk to remove the managed block
awk -v begin="$MARK_BEGIN" -v end="$MARK_END" '
BEGIN { inblock=0 }
{
if ($0 ~ begin) { inblock=1; next }
if ($0 ~ end) { inblock=0; next }
if (!inblock) print $0
}
' "$file" > "${file}.tmp"
mv "${file}.tmp" "$file"
ok "Removed uvx aliases block from: $file"
else
info "No managed block found in: $file"
fi
}
install_block() {
local file="$1"
local pyver="$2"
# Prepare the block content
read -r -d '' BLOCK <<EOF
${MARK_BEGIN}
# Default Python version for uvx-based aliases
export UVX_DEFAULT_PYTHON="${pyver}"
# Resolve effective version: use ./.python-version if present, else UVX_DEFAULT_PYTHON
__uvx_resolve_ver() {
local v
if [[ -f ".python-version" ]]; then
v="\$(cat .python-version | tr -d '[:space:]')"
[[ -n "\$v" ]] || v="\${UVX_DEFAULT_PYTHON}"
else
v="\${UVX_DEFAULT_PYTHON}"
fi
printf "%s" "\$v"
}
# Aliases implemented as shell functions so we can pass arguments
__uvx_python() {
local ver="\$(__uvx_resolve_ver)"
command uvx --python "\$ver" python "\$@"
}
__uvx_pip() {
local ver="\$(__uvx_resolve_ver)"
command uvx --python "\$ver" pip "\$@"
}
__uvx_ipy() {
local ver="\$(__uvx_resolve_ver)"
command uvx --python "\$ver" --with ipython ipython "\$@"
}
__uvx_jlab() {
local ver="\$(__uvx_resolve_ver)"
command uvx --python "\$ver" --with jupyterlab jupyter lab "\$@"
}
alias python="__uvx_python"
alias pip="__uvx_pip"
alias ipy="__uvx_ipy"
alias jlab="__uvx_jlab"
alias uvcache='ls "\$HOME/.cache/uv/python" 2>/dev/null || echo "No uv python cache found."'
${MARK_END}
EOF
# Ensure file exists
touch "$file"
# Remove old block if exists, then append new one
if grep -Fq "$MARK_BEGIN" "$file"; then
info "Existing managed block detected; replacing it."
remove_block "$file"
fi
backup_file "$file"
{
echo ""
echo "$BLOCK"
echo ""
} >> "$file"
ok "Installed uvx aliases into: $file"
}
print_post_install() {
local file="$1"
cat <<EOM
Next steps:
1) Reload your shell config:
source "$file"
# 또는 터미널을 새로 열기
2) Test:
python -V # should run via uvx (e.g., Python ${DEFAULT_PY}.x)
pip --version # via uvx
ipy # IPython via uvx
jlab # JupyterLab via uvx
3) Per-project version:
echo "3.11" > .python-version
python -V # now uses 3.11 in this dir
Uninstall later:
./setup_uvx_alias.sh --uninstall${RCFILE:+ --rcfile "$RCFILE"}
EOM
}
# ---------- Parse args ----------
while [[ $# -gt 0 ]]; do
case "$1" in
--python)
shift
[[ $# -gt 0 ]] || { err "--python requires a value (e.g., 3.12)"; exit 1; }
DEFAULT_PY="$1"
;;
--rcfile)
shift
[[ $# -gt 0 ]] || { err "--rcfile requires a path value"; exit 1; }
RCFILE="$1"
;;
--uninstall)
UNINSTALL="1"
;;
-h|--help)
sed -n '1,120p' "$0"
exit 0
;;
*)
err "Unknown argument: $1"
exit 1
;;
esac
shift
done
RC_PATH="$(detect_shell_rc)"
if [[ "$UNINSTALL" == "1" ]]; then
info "Uninstalling uvx aliases from: $RC_PATH"
remove_block "$RC_PATH"
ok "Done."
exit 0
fi
if ! has_uv; then
err "uv is not on PATH. Please install uv first: https://github.com/astral-sh/uv"
exit 1
fi
info "Installing uvx aliases into: $RC_PATH"
install_block "$RC_PATH" "$DEFAULT_PY"
print_post_install "$RC_PATH"
@ngenieer
Copy link
Author

ngenieer commented Oct 30, 2025

1) Check permissions to execute downloaded scripts (already set)

chmod +x setup_uvx_alias.sh

2) Install with default (Python 3.12)

./setup_uvx_alias.sh

3) If you want to use zsh or specify a specific rc file

./setup_uvx_alias.sh --rcfile ~/.zshrc

4) If you want to change the default Python version

./setup_uvx_alias.sh --python 3.11
./setup_uvx_alias.sh --uninstall

Apply immediately after installation

source ~/.bashrc # 또는 zsh면 source ~/.zshrc

python -V # uvx 기반 Python 실행
pip --version # uvx 환경 pip
ipy # IPython
jlab # JupyterLab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment