Skip to content

Instantly share code, notes, and snippets.

@kishannareshpal
Last active June 30, 2025 21:11
Show Gist options
  • Save kishannareshpal/342efc4a15e47ea5d338784d3e9a8d98 to your computer and use it in GitHub Desktop.
Save kishannareshpal/342efc4a15e47ea5d338784d3e9a8d98 to your computer and use it in GitHub Desktop.
Automatically activate python virtual environment when changing directory. In other words, it does `source ./venv/bin/activate` for you!

This is a custom shell script I wrote in order to ease my workflow when dealing with Python Virtual Environments.

Installation

Download the activatevenv.plugin.sh into your computer.

oh-my-zsh

  1. Download the script into $ZSH_CUSTOM/plugins/<plugin_name>/activatevenv/. See oh-my-zsh Plugin Customization Wiki
  # Use curl or download manually from https://git.io/JLBHr
  (mkdir activatevenv; cd activatevenv) && curl -L --url https://git.io/JLBHr --output ./activatevenv/activatevenv.plugin.zsh
  1. Edit your ~/.zshrc:
# Add activatevenv to your list of plugins and restart the terminal.
  plugins=(... , activatevenv)
  …

Manually

  1. Copy and paste the activatevend.plugin.sh script to the bottom of your ~/.bashrc or ~/.zshrc and restart your terminal.

Usage

Simply cd into a project directory with a python virtual environment setup (with any of these names: venv/, .venv/, env or .env), and the script will activate it automatically for you (just as you would do with source ./venv/bin/activate).

If you are creating a new virtualenv, run python -m <venv|virtualenv> <venv|.venv|.env|env> in your root directory and to activate it manually call activatevenv (also when you cd back into your project folder it will automatically activate it).

# Automatically activates venv if ./venv/ exists in curent directory
# Go to your project folder, run "pip virtualenv <.venv|venv>", so your project folder
# has a <.venv|venv> folder at the top level
# .
# ├── <other_project_files>
# ├── ...
# └── <.venv|venv|.env|env>
# ├── bin
# ├── include
# └── lib
#
# The virtualenv will be activated automatically when you enter the directory.
# To deactivate run `deactivate` and to manually activate run `activatevenv`
function activatevenv() {
# Names of possible virtualenv directories
VIRTUALENV_DIRS=("venv/" "env/" ".env/" ".venv/" "${PWD##*/}")
for dir in "${VIRTUALENV_DIRS[@]}"; do
if [[ -d "${dir}" ]]; then
# Found a possible venv directory
# Try activating the venv
if [[ -e "./${dir}/bin/activate" ]]; then
source ./$dir/bin/activate
echo "Virtual environment activated automatically"
break
fi
fi
done
}
activatevenv
# Extension for `cd` command in order to automatically activate virtualenv when changin directories.
function cd() {
builtin cd $1
# Try activating venv
activatevenv
}
@DreadPirateShawn
Copy link

Note: Need to change for dir in $VIRTUALENV_DIRS; do to for dir in ${VIRTUALENV_DIRS[@]}; do to handle checking all variations, rather than just the first one.

@kishannareshpal
Copy link
Author

kishannareshpal commented Feb 23, 2025

Note: Need to change for dir in $VIRTUALENV_DIRS; do to for dir in ${VIRTUALENV_DIRS[@]}; do to handle checking all variations, rather than just the first one.

Hi @DreadPirateShawn, good catch. I've updated the script to reflect this change. Thank you

@filiphanes
Copy link

need to deactivate it when cd outside

@thesmart
Copy link

@filiphanes @kishannareshpal Here is my version that will automatically deactivate when cd outside:

# Activates venv if ./venv/ exists in when you enter the directory.
#
#   ├── <other_project_files>
#   ├── ...
#   └── <.venv|venv|.env|env>
#       ├── bin
#       ├── include
#       └── lib
#
function activatevenv() {
  # Names of possible virtualenv directories
  VIRTUALENV_DIRS=("venv/" "env/" ".env/" ".venv/" "${PWD##*/}")

  # Variable to store the found venv directory
  FOUND_VENV_DIR=""

  for dir in "${VIRTUALENV_DIRS[@]}"; do
    if [[ -d "${dir}" ]]; then
      # Found a possible venv directory
      # Check if it has an activate script
      if [[ -e "./${dir}/bin/activate" ]]; then
        FOUND_VENV_DIR="${dir}"
        break
      fi
    fi
  done

  # If we found a valid venv directory, activate it
  if [[ -n "${FOUND_VENV_DIR}" ]]; then
    source ./${FOUND_VENV_DIR}/bin/activate
    echo "Virtual environment activated automatically"
  fi
}

# Deactivates venv if $VIRTUAL_ENV is set.
function deactivatevenv() {
  # Check if a virtual environment is currently activated
  if [ -n "${VIRTUAL_ENV}" ]; then
    deactivate
    echo "Virtual environment deactivated"
  fi
}

# Extension for `cd` command in order to automatically activate virtualenv when changin directories.
function cd() {
  deactivatevenv
  builtin cd $1
  # Try activating venv
  activatevenv
}

@andreadellacorte
Copy link

this will work with cd as well as z + set a prompt showing the current is active (based on robbyrussell theme)

# --- Auto-activate .venv in current/parent directory ---

function activatevenv() {
  local dir="$PWD"
  local venv_path=""
  local previous="$VIRTUAL_ENV"

  while [[ "$dir" != "/" ]]; do
    if [[ -f "$dir/.venv/bin/activate" ]]; then
      venv_path="$dir/.venv"
      break
    fi
    dir="$(dirname "$dir")"
  done

  # Activate new venv
  if [[ -n "$venv_path" ]]; then
    if [[ "$VIRTUAL_ENV" != "$venv_path" ]]; then
      [[ -n "$VIRTUAL_ENV" ]] && deactivate >/dev/null
      source "$venv_path/bin/activate" >/dev/null
      echo "Activated: $venv_path"
    fi
  else
    # No venv found, deactivate if needed
    if [[ -n "$VIRTUAL_ENV" ]]; then
      deactivate >/dev/null
      echo "Deactivated virtual environment"
    fi
  fi
}

# Hook to run before every prompt
autoload -U add-zsh-hook
add-zsh-hook precmd activatevenv

# Override cd
function cd() {
  builtin cd "$@" && activatevenv
}

# Override z (if defined later by plugin)
if command -v z >/dev/null; then
  function z() {
    builtin z "$@" && activatevenv
  }
fi

# Only run once on shell startup to avoid duplicate triggers
if [[ -z "$ZSH_VENV_INITIALISED" ]]; then
  activatevenv
  export ZSH_VENV_INITIALISED=1
fi

# Override robbyrussell prompt AFTER theme is loaded
function set_custom_prompt() {
  PROMPT='$( [[ -n "$VIRTUAL_ENV" ]] && echo "%{$fg[green]%}($(basename "$VIRTUAL_ENV"))%{$reset_color%} " )%{$fg_bold[green]%}➜ %{$fg[cyan]%}%c%{$reset_color%} $(git_prompt_info)'
}
add-zsh-hook precmd set_custom_prompt

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