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

后端 未结 12 1936
北海茫月
北海茫月 2020-12-24 06:27

I have a bunch of projects in my ~/Documents. I work almost exclusively in python, so these are basically all python projects. Each one, e.g. ~/Documents/

相关标签:
12条回答
  • 2020-12-24 06:32

    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.

    0 讨论(0)
  • 2020-12-24 06:32

    that is the solution without cd'ing, with zsh set to setop auto_cd w'll be able to change directories without cd, just type directory name and hit enter. it is anhence of above solution:

        # auto activate virtualenv
    # Modified solution based on https://stackoverflow.com/questions/45216663/how-to-automatically-activate-virtualenvs-when-cding-into-a-directory/56309561#56309561
    function auto_active_env() {
    
      ## Default path to virtualenv in your projects
      DEFAULT_ENV_PATH="./env"
    
      ## If env folder is found then activate the vitualenv
      function activate_venv() {
        if [[ -f "${DEFAULT_ENV_PATH}/bin/activate" ]] ; then 
          source "${DEFAULT_ENV_PATH}/bin/activate"
          echo "Activating ${VIRTUAL_ENV}"
        fi
      }
    
      if [[ -z "$VIRTUAL_ENV" ]] ; then
        activate_venv
      else
        ## check the current folder belong to earlier VIRTUAL_ENV folder
        # if yes then do nothing
        # else deactivate then run a new env folder check
          parentdir="$(dirname ${VIRTUAL_ENV})"
          if [[ "$PWD"/ != "$parentdir"/* ]] ; then
            echo "Deactivating ${VIRTUAL_ENV}"
            deactivate
            activate_venv
          fi
      fi
    }
    chpwd_functions=(${chpwd_functions[@]} "auto_active_env")
    
    0 讨论(0)
  • 2020-12-24 06:33

    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!...

    0 讨论(0)
  • 2020-12-24 06:33

    This is a zsh only solution.

    This is an improvement over daveruinseverything's answer which is an improvement over MS_'s answer.

    We are using precmd hook instead of overwriting cd.

    We have added another extra feature. Suppose the directory structure is

    ├── .venv
    │   ├── bin
    │   │   └── activate
    ├── subdir
    │   ├── subdir1
    │   │   ├── subdir2
    │   │   │   └── test2.txt
    │   │   └── test1.txt
    │   └── test.txt
    └── testing.py
    

    If you now open new terminal in subdir2, or directly cd to subdir2 from other place, it will activate the venv.

    The solution is:

    autoload -Uz add-zsh-hook
    add-zsh-hook precmd automatically_activate_python_venv
    
    function automatically_activate_python_env() {
      if [[ -z $VIRTUAL_ENV ]] ; then
        activate_venv
      else
        parentdir="$(dirname ${VIRTUAL_ENV})"
        if [[ "$PWD"/ != "$parentdir"/* ]] ; then
          deactivate
          activate_venv
        fi
      fi
    }
    
    function activate_venv() {  
      local d n
      d=$PWD
      
      until false 
      do 
      if [[ -f $d/.venv/bin/activate ]] ; then 
        source $d/.venv/bin/activate
        break
      fi
        d=${d%/*}
        # d="$(dirname "$d")"
        [[ $d = *\/* ]] || break
      done
    }
    
    0 讨论(0)
  • 2020-12-24 06:41

    This is my solution:

    1. If VIRTUAL_ENV is not set then:
      1. Check if we're inside a virtual env
      2. If yes, then activate it
    2. Else (VIRTUAL_ENV is defined), check that the current folder starts with $VIRTUAL_ENV (removing the /venv part) and verify that the deactivate command exists
      1. Deactivate teh environment

    This is the script:

    function cd() {
      builtin cd $1
    
      if [[ -z "${VIRTUAL_ENV}" ]]; then
        if [[ -d ./venv && -f ./venv/bin/activate ]]; then
          source ./venv/bin/activate
        fi
      elif [[ ! "$(pwd)" == ${VIRTUAL_ENV:0:n-5}* && ! -z "$(command -v deactivate)" ]]; then
        deactivate
      fi
    }
    

    Note: You need to add this to .bashrc. If it doesn't work, check if your .profile is not overriding your command (it happened to me)

    0 讨论(0)
  • 2020-12-24 06:42

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

    For example:

    plugins=(
      git pip python brew virtualenvwrapper
    )
    
    0 讨论(0)
提交回复
热议问题