ยง Activating poetry venvs with fzf

More often than not I find myself wanting to reuse a python virtual environment with packages I use frequently already installed. For instance, I might want to do some quick calculations and plotting in a scratch directory with numpy, scipy, pandas, matplotlib, etc. With my current python workflow, it is easy to lie on a Procrustean bed and create a new virtual environment in each and every new scratch directory. Alternatively, one could create a base virtual environment, install the aforementioned packages and source the virtual environment from where ever it is located and carry on with one’s calculations and plots.

This latter option is slightly less cumbersome than the former, but it still requires you to know where the base virtual environment is located. With poetry and fzf we can lift the mental overhead of having to knowing where the base virtual environment is. To this end, I wrote a smol script that uses fzf to source a poetry virtual environment from anywhere.


#!/usr/bin/env bash

# Constants
VENV_BASE_DIR="${HOME}/.local/share/pyproject/virtualenvs"

# Error handling
if [ ! -d "$VENV_BASE_DIR" ]; then
    echo "Error: Poetry virtualenvs directory not found at $VENV_BASE_DIR" >&2
    exit 1
fi

# Check for fzf
if ! command -v fzf >/dev/null 2>&1; then
    echo "Error: fzf is not installed. Please install it first." >&2
    exit 1
fi

# Function to extract project name from virtualenv path
get_project_name() {
    local venv_name="$1"
    basename "$venv_name" | sed -E 's/-[^-]*-py[0-9.]+$//'
}

# Function to list and format virtualenvs
list_virtualenvs() {
    local venv_dir
    while IFS= read -r venv_dir; do
        # Skip if not a directory
        [ ! -d "$venv_dir" ] && continue
        # Skip if no activation script exists
        [ ! -f "$venv_dir/bin/activate" ] && continue
        
        # Get and output project name
        get_project_name "$venv_dir"
    done < <(find "$VENV_BASE_DIR" -mindepth 1 -maxdepth 1 -type d)
}

# Function to find virtualenv path from project name
find_venv_path() {
    local project_name="$1"
    local venv_dir
    
    while IFS= read -r venv_dir; do
        [ ! -d "$venv_dir" ] && continue
        if [ "$(get_project_name "$venv_dir")" = "$project_name" ]; then
            echo "$venv_dir"
            return 0
        fi
    done < <(find "$VENV_BASE_DIR" -mindepth 1 -maxdepth 1 -type d)
    
    return 1
}

# Main execution
main() {
    # Get list of projects and select with fzf
    local selected_project
    selected_project=$(list_virtualenvs | sort | fzf --prompt="Select virtualenv: ")
    
    # Exit if nothing selected
    [ -z "$selected_project" ] && exit 0
    
    # Find corresponding virtualenv path
    local venv_path
    venv_path=$(find_venv_path "$selected_project")
    
    if [ -z "$venv_path" ]; then
        echo "Error: Could not find virtualenv for $selected_project" >&2
        exit 1
    fi
    
    # Check if activation script exists
    if [ ! -f "$venv_path/bin/activate" ]; then
        echo "Error: Activation script not found in $venv_path" >&2
        exit 1
    fi 
    
    # Activate virtualenv and start new shell
    . "$venv_path/bin/activate"
    exec "$SHELL"
}

main

From here you can dump this into $HOME/.local/bin and/or bind the execution of the script to a shell keyboard shortcut.