Skip to content

Instantly share code, notes, and snippets.

@xmodar
Last active October 31, 2024 03:13
Show Gist options
  • Save xmodar/7bcb7cbcc9a263ef8f758e1bad9a80eb to your computer and use it in GitHub Desktop.
Save xmodar/7bcb7cbcc9a263ef8f758e1bad9a80eb to your computer and use it in GitHub Desktop.
Manage Python virtual environments with uv (astral.sh/uv)
# Install this tool with the followig command:
# curl -LsSf https://gist.githubusercontent.com/xmodar/7bcb7cbcc9a263ef8f758e1bad9a80eb/raw/uvn.sh | bash
uvn() {
if [[ "$1" == "-h" ]]; then
echo "Manage Python virtual environments with uv (astral.sh/uv)"
echo ""
echo "Author: @xmodar"
echo "Link : https://gist.github.com/xmodar/7bcb7cbcc9a263ef8f758e1bad9a80eb"
echo "Also : https://github.com/xmodar/uvn"
echo ""
echo "Usage:"
echo " uvn # List environments"
echo " uvn [<env>|.] # Activate environment"
echo " uvn -d # Deactivate environment"
echo " uvn -v <env> [-q] # Get environment version"
echo " uvn -n <env> # Create new environment"
echo " uvn -r <env> # Remove environment"
echo " uvn -e <env> # Export as requirements.txt"
echo " uvn -s <env> # Export as inline script metadata"
echo " uvn -c <env> <new> # Clone environment"
echo " uvn -h # Display this help message"
return 0
fi
local venv_dir=~/.virtualenvs
# Check if uv exists
command -v uv &>/dev/null || {
echo -e "Error: 'uvn' requires 'uv'. Install it with:\n\n"\
" curl -LsSf https://astral.sh/uv/install.sh | sh\n"
return 1
}
# List available environments
if [[ $# -eq 0 ]]; then
for env_path in "$venv_dir"/*; do
name=$(basename "$env_path")
version=$(uvn -v "$name" -q)
if [[ -n "$version" ]]; then
active=$([[ $VIRTUAL_ENV == $env_path ]] && echo "*" || echo "")
echo -e "$name$active\t@ $version"
fi
done
# Activate the specified environment
elif [[ $# -eq 1 ]]; then
if [[ "$1" == '-d' ]]; then
deactivate
else
if [[ "$1" == "." ]]; then
env_path="$(pwd)/.venv/bin/activate"
else
env_path="$venv_dir/$1/bin/activate"
fi
if [[ -f $env_path ]]; then
source "$env_path"
else
echo "Environment '$1' does not exist" >&2
return 1
fi
fi
# Get the specified environment version
elif [[ "$1" == "-v" && -n "$2" ]]; then
version=$("$venv_dir/$2/bin/python" --version 2>/dev/null | cut -c 8-)
if [[ -n "$version" ]]; then
echo "$version"
else
[[ "$3" != "-q" ]] && echo "Environment '$2' does not exist" >&2
return 1
fi
# Create a new environment
elif [[ "$1" == "-n" && -n "$2" ]]; then
if [[ -n $(uvn -v $2 -q) ]]; then
echo "Environment '$2' already exists" >&2
return 1
fi
result=$(uv venv "$venv_dir/$2" "${@:3}" 2>&1)
version=$(uvn -v "$2" -q)
if [[ -n "$version" ]]; then
echo "Created environment '$2' @ "$version""
else
echo "$result"
return 1
fi
# Remove the specified environment
elif [[ "$1" == "-r" && -n "$2" ]]; then
if [[ -n $(uvn -v $2 -q) ]]; then
rm -rf "$venv_dir/$2" && echo "Removed environment '$2'"
else
echo "Environment '$2' does not exist" >&2
return 1
fi
# Export the specified environment
elif [[ "$1" == "-e" && -n "$2" ]]; then
if [[ -n $(uvn -v $2 -q) ]]; then
echo "# python==$(uvn -v "$2")" # Add Python version as a comment
VIRTUAL_ENV="$venv_dir/$2" uv pip freeze 2>/dev/null
else
echo "Environment '$2' does not exist" >&2
return 1
fi
# Export the specified environment as inline script metadata
elif [[ "$1" == "-s" && -n "$2" ]]; then
version=$(uvn -v $2 -q)
if [[ -n "$version" ]]; then
echo "#!/usr/bin/env -S uv run"
echo "# /// script"
next_version=$(echo "$version" | awk -F. '{print $1"."$2+1}')
echo "# requires-python = \">="$version",<$next_version\""
requirements=$(VIRTUAL_ENV="$venv_dir/$2" uv pip tree -d 0 2>/dev/null)
if [[ -n "$requirements" ]]; then
echo "# dependencies = ["
echo "$requirements" | awk '{gsub(/^v/, "", $2); printf "# \"%s>=%s\",\n", $1, $2}'
echo "# ]"
fi
echo "# ///"
else
echo "Environment '$2' does not exist" >&2
return 1
fi
# Clone the specified environment
elif [[ "$1" == "-c" && -n "$2" && -n "$3" ]]; then
if [[ -n $(uvn -v "$3" -q) ]]; then
echo "Environment '$3' already exists" >&2
return 1
fi
version=$(uvn -v "$2" -q)
if [[ -n "$version" ]]; then
uvn -n "$3" -p "$version"
uvn -e "$2" | VIRTUAL_ENV="$venv_dir/$3" uv pip install -r -
else
echo "Environment '$2' does not exist" >&2
return 1
fi
# If no valid option is matched
else
uvn -h
return 1
fi
}
add_bash_function() {
local func="$(type "$1" | tail -n +2)"
local name=$(echo "${func%% *}")
local target=""$HOME"/.bash_functions/"$name".sh"
mkdir -p "$(dirname "$target")"
echo "$func" > "$target"
local parent=$(dirname "$target")
local body="for file in "$parent"/*.sh; do source \$file; done"
local block=$(echo -e "# /- "$parent"\n"$body"\n# "$parent" -/")
if ! grep -qF "$block" ~/.bashrc; then
echo "$block" >> ~/.bashrc
fi
}
add_bash_function uvn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment