How to automatically activate virtualenvs when cd'ing into a directory

Solution 1:

Add following in your .bashrc or .zshrc

function cd() {
  builtin cd "$@"

  if [[ -z "$VIRTUAL_ENV" ]] ; then
    ## If env folder is found then activate the vitualenv
      if [[ -d ./.env ]] ; then
        source ./.env/bin/activate
      fi
  else
    ## check the current folder belong to earlier VIRTUAL_ENV folder
    # if yes then do nothing
    # else deactivate
      parentdir="$(dirname "$VIRTUAL_ENV")"
      if [[ "$PWD"/ != "$parentdir"/* ]] ; then
        deactivate
      fi
  fi
}

This code will not deactivate the virtualenv even if someone goes into subfolder. Inspired by answers of @agnul and @Gilles.

If the virtualenv is made by pipenv, then please consider this wiki page.

Furthermore, for added security please consider direnv.

Solution 2:

Put something like this in your .zshrc

function cd() {
  if [[ -d ./venv ]] ; then
    deactivate
  fi

  builtin cd $1

  if [[ -d ./venv ]] ; then
    . ./venv/bin/activate
  fi
}

Edit: As noted in comments cd-ing into a subfolder of the current virtual env would deactivate it. One idea could be to deactivate the current env only if cd-ing into a new one, like

function cd() {
  builtin cd $1

  if [[ -n "$VIRTUAL_ENV" && -d ./venv ]] ; then
    deactivate
    . ./venv/bin/activate
  fi
}

that could still be improved, maybe turning it into a "prompt command" or attempting some prefix matching on the folder names to check there's a virtual env somewhere up the path, but my shell-fu is not good enough.

Solution 3:

Rather than writing a custom script you could use direnv. It's not a zsh specific solution (for that you could try zsh-autoenv), but is well-maintained and easy to use with zsh. Once you've installed it, you'd want to put eval "$(direnv hook zsh)" at the end of your .zshrc. At that point you can do:

$ source ~/.zshrc
$ cd foo
$ echo "layout python" > .envrc
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
$ direnv allow
direnv: loading .envrc
direnv: export +VIRTUAL_ENV ~PATH

Now you should be in your virtualenv. You can test by running pip freeze to see that your virtualenv specific packages are installed. To deactivate

$ cd ..
direnv: unloading

Solution 4:

By far the easiest option (in 2019+) is to add virtualenvwrapper into your ~/.zshrc plugins

For example:

plugins=(
  git pip python brew virtualenvwrapper
)

Solution 5:

You should try something like autoenv if not direnv.

The first one is considered to be "lightweight", while the second one "simply, higher quality software", listening respectively to each one's author, talking about the other one's project. Thus, they seem to me fairly good options, to try both!

Anyway, both have been tested on zsh shells. In particular, autoenv is really simple to use, after installing it:

$ git clone git://github.com/inishchith/autoenv.git ~/.autoenv
$ echo 'source ~/.autoenv/activate.sh' >> ~/.bashrc

just "follow the white rabbit " and try for example

$ mkdir project
$ echo "echo 'whoa'" > project/.env
$ cd project
whoa

"If a directory contains a .env file, it will automatically be executed when you cd into it. When enabled (set AUTOENV_ENABLE_LEAVE to a non-null string), if a directory contains a .env.leave file, it will automatically be executed when you leave it."

Have a look at https://github.com/inishchith/autoenv for more detailed instructions!...