Как автоматически активировать virtualenvs, когда cd'ing в каталог

У меня есть куча проектов в моих ~/Documents. Я работаю почти исключительно на Python, так что это в основном все проекты Python. Каждый, например, ~/Documents/foo имеет свой собственный virtualenv, ~/Documents/foo/venv (их всегда называют venv). Всякий раз, когда я переключаюсь между проектами, что составляет ~ 10 раз в день, я делаю

deactivate
cd ..
cd foo
source venv/bin/activate

Я дошел до того, что мне надоело вводить deactivate и использовать source venv/bin/activate. Я ищу способ просто cd../foo и обработать операции virtualenv для меня.

  • Я знаком с VirtualEnvWrapper, который, на мой взгляд, немного жесток. Кажется, что он перемещает все ваши virtualenvs куда-то еще и добавляет немного больше сложности, чем удаляет, насколько я могу судить. (Особые мнения приветствуются!)

  • Я не слишком знаком со сценариями оболочки. Если вы порекомендуете добавить в мой ~/.zshrc скрипт с низким уровнем обслуживания, который бы этого достиг, этого было бы более чем достаточно, но из-за быстрого поиска, я не нашел такого скрипта.

  • Я пользователь zsh/ oh-my-zsh. oh-my-zsh, кажется, не есть плагин для этого. Лучшим ответом на этот вопрос был бы кто-то, кто oh-my-zsh плагин oh-my-zsh который делает это. (Что я мог бы сделать, если ответы здесь тусклые.

Ответ 1

Поместите что-то подобное в ваш.zshrc

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

  builtin cd $1

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

Изменить: как отмечено в комментариях, cd -ing в подпапку текущего виртуального env дезактивирует его. Одной из идей может быть деактивация текущего env, только если cd -ing в новый, например

function cd() {
  builtin cd $1

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

который все еще может быть улучшен, возможно, превратив его в "подсказку" или попытку сопоставления префикса в именах папок, чтобы проверить там виртуальный env где-то по пути, но мой shell-fu недостаточно хорош.

Ответ 2

Добавьте следующее в ваш .bashrc или .zshrc

function cd() {
  builtin cd "[email protected]"

  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
}

Этот код не деактивирует virtualenv, даже если кто-то перейдет в подпапку. Вдохновленные ответами @agnul и @Gilles.

Кроме того, для дополнительной безопасности рассмотрите direnv.

Ответ 3

Вместо написания собственного скрипта вы можете использовать direnv. Это не специфичное для zsh решение (для этого вы можете попробовать zsh-autoenv), но оно хорошо поддерживается и прост в использовании с zsh. После того, как вы установили его, вы захотите поместить eval "$(direnv hook zsh)" в конец вашего .zshrc. На этом этапе вы можете сделать:

$ 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

Теперь вы должны быть в своем виртуальности. Вы можете проверить, запустив pip freeze чтобы убедиться, что ваши пакеты virtualenv установлены. Деактивировать

$ cd ..
direnv: unloading

Ответ 4

Это мое решение, которое:

  • проверяет, есть ли уже на текущем активном venv, и ничего не делает в этом случае
  • если есть папка venv, деактивируйте активную, если она есть
  • активировать новый venv независимо от того, был ли старый или нет.

В моем bash_aliases:

function cd() {
    builtin cd "[email protected]"

    if [ $(dirname "$VIRTUAL_ENV") == $(pwd) ] ; then
        # Already at the active virtual env
        return
    fi

    if [[ -d ./venv ]] ; then
        if type deactivate > /dev/null 2>&1 ; then
            printf "Deactivating virtualenv %s\n" "$VIRTUAL_ENV"
            deactivate
        fi

        source ./venv/bin/activate
        printf "Setting up   virtualenv %s\n" "$VIRTUAL_ENV"
    fi
}

Ответ 5

Для потомков: я использовал решение @MS_, но столкнулся с проблемой, при которой cd непосредственно из одного проекта в другой деактивирует старый virtualenv, но не активирует новый. Это слегка измененная версия этого решения, которое решает эту проблему:

# 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 cd() {
  builtin cd "[email protected]"

  ## 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
}

Ответ 6

это решение без cd'ing, с zsh, установленным в setop auto_cd, мы сможем изменять каталоги без cd, просто введите имя каталога и нажмите Enter. это преимущество вышеуказанного решения:

    # 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")

Ответ 7

Вот (еще) другое решение для автоматической активации виртуальной среды; это основано на ряде ответов, уже размещенных здесь.

Это будет работать для любого имени или каталога виртуальной среды (не только для ./env, ./venv и т.д.). Также поддерживает подкаталоги, а также cd -ing в символические ссылки папок виртуальной среды (родительских).

Этот код ищет файл pyvenv.cfg вместо определенного именованного каталога. Если он найден в подкаталоге текущей папки, среда автоматически активируется. Находясь в виртуальной среде, это состояние сохраняется до тех пор, пока вы не выйдете из каталога родительской виртуальной среды, после чего среда будет деактивирована.

Поместите это в свой .bashrc или .bash_profile.

function cd() {
  builtin cd "[email protected]"

  if [[ -z "$VIRTUAL_ENV" ]] ; then
      # If config file is found -> activate the vitual environment
      venv_cfg_filepath=$(find . -maxdepth 2 -type f -name 'pyvenv.cfg' 2> /dev/null)
      if [[ -z "$venv_cfg_filepath" ]]; then
        return # no config file found
      fi

      venv_filepath=$(cut -d '/' -f -2 <<< ${venv_cfg_filepath})
      if [[ -d "$venv_filepath" ]] ; then
        source "${venv_filepath}"/bin/activate
      fi
  else
    # If the current directory is not contained 
    # within the venv parent directory -> deactivate the venv.
      cur_dir=$(pwd -P)
      venv_dir="$(dirname "$VIRTUAL_ENV")"
      if [[ "$cur_dir"/ != "$venv_dir"/* ]] ; then
        deactivate
      fi
  fi
}

Лично я считаю, что это улучшение для многих решений, поскольку оно должно работать в любой виртуальной среде.