Skip to main content
  1. Dispatches/

TIL - Automatically Set Your virtualenv

·321 words·2 mins
TIL python poetry hatch venv direnv

I’m a big fan of using direnv to set project-specific env variables. And at one point I was using it to automatically activate my poetry virtualenv whenever I entered the directory per this function. However, lately I’ve also been playing with hatch or just calling python -m venv directly, so I wanted to work out a way to have a unified approach.

This is what I came up with, and I’m sure there is a more elegant way to do this, but it works, and that’s what matters for now.

First, you add the following functions to ~/.config/direnv/direnvrc:

layout_env() {
  if [[ ! -f pyproject.toml ]]; then
    log_error 'No pyproject.toml found. Cannot determine appropriate env to activate.'
    exit 2
  fi

  grep -q poetry pyproject.toml
  retVal=$?
  if [ $retVal -ne 0 ]; then
     grep -q hatch pyproject.toml
     hatchVal=$?
     if [ $hatchVal -ne 0 ]; then
       layout_venv
     else
       layout_hatch
     fi
  else
    layout_poetry
  fi

}
layout_poetry() {
  if [[ ! -f pyproject.toml ]]; then
    log_error 'No pyproject.toml found.  Use `poetry new` or `poetry init` to create one first.'
    exit 2
  fi

  local VENV=$(dirname $(poetry run which python))
  export VIRTUAL_ENV=$(echo "$VENV" | rev | cut -d'/' -f2- | rev)
  export POETRY_ACTIVE=1
  PATH_add "$VENV"
}

layout_hatch() {
  if [[ ! -f pyproject.toml ]]; then
    log_error 'No pyproject.toml found! Create one first using your preferred tools.'
    exit 2
  fi
  
  local VENV=$(dirname $(hatch -q run which python))
  export VIRTUAL_ENV=$(echo "$VENV" | rev | cut -d'/' -f2- | rev)
  export HATCH_ENV_ACTIVE="default"
  PATH_add "$VENV"
}

layout_venv() {
  if [[ ! -d .venv ]]; then
    log_error 'No local virtual env exists yet. Create one using python -m venv --prompt . .venv'
    exit 2
  fi

  local VENV=".venv/bin"
  export VIRTUAL_ENV=$(echo "$VENV" | rev | cut -d'/' -f2- | rev)
  PATH_add "$VENV"
}

Then in any project directory’s .envrc add a single line:

layout_env

After making your changes, run direnv allow to approve the modified file for use and you’ll be good to go!