Как автоматически активировать virtualenvs при переходе в каталог

У меня есть куча проектов в моем ~/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 плагин, который делает это. (Что я мог бы сделать, если ответы здесь тусклые.

23 ответа

Решение

Положите что-то вроде этого в вашем.zshrc

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

  builtin cd $1

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

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

function cd() {
  builtin cd $1

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

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

Добавьте следующее в ваш.bashrc или.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
}

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

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

Вам следует попробовать что-то вроде autoenv, если не direnv.

Первый считается "легковесным", а второй - "просто более качественным программным обеспечением", который соответственно слушает каждого автора и рассказывает о проекте другого. Таким образом, они кажутся мне довольно хорошими вариантами, чтобы попробовать оба!

Во всяком случае, оба были протестированы на zshснаряды. Особенно,autoenv действительно прост в использовании, после его установки:

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

просто "следуй за белым кроликом " и попробуйте, например,

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

"Если каталог содержит .env файл, он будет автоматически выполнен, когда вы cdвнутрь. При включении (установитьAUTOENV_ENABLE_LEAVE в ненулевую строку), если каталог содержит .env.leave файл, он будет автоматически выполнен, когда вы его покинете ".

Загляните на https://github.com/inishchith/autoenv для получения более подробных инструкций!...

Вместо написания собственного скрипта вы можете использовать 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

По далеко самый простой вариант (в 2019+) является добавлениеvirtualenvwrapper в ваш ~/.zshrc plugins

Например:

plugins=(
  git pip python brew virtualenvwrapper
)

Для всех, кто использует (или рассматривает возможность использования) pyenv, это может быть очень удобно достигнуто с помощью плагина pyenv-virtualenv, как описано здесь .

Обычно вы просто добавляете .python-version файл в каталог, в котором указано имя virtualenv.

Это решение только для zsh.

Это улучшение по сравнению с ответом daveruinseverything, которое является улучшением по сравнению с ответом MS_.

Мы используем precmd перехватить вместо перезаписи cd.

Мы добавили еще одну дополнительную функцию. Предположим, что структура каталогов

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

Если вы сейчас откроете новый терминал в subdir2 или напрямую перейдете в subdir2 из другого места, он активирует venv.

Решение такое:

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
}

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

# auto activate virtualenv
# Modified solution based on https://stackru.com/questions/45216663/how-to-automatically-activate-virtualenvs-when-cding-into-a-directory/56309561#56309561
function cd() {
  builtin cd "$@"

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

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

    # auto activate virtualenv
# Modified solution based on https://stackru.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")

Аналогично ответу Джейка, но поддерживает cdпереход от одного виртуального окружения к другому. В этом случае он деактивирует старый virtualenv, а затем активирует новый.

      function cd() {
  builtin cd "$@"

  if [[ ! -z "$VIRTUAL_ENV" ]] ; then
    # 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

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

Для такого разработчика Python, как я, я использую этот плагин для создания, активации виртуальных сред Python при входе в проект Python, он также деактивируется после cdв другой каталог.

В настоящее время есть небольшая ошибка, из-за которой ваша тема ZSH вернется к значениям по умолчанию после деактивации, и здесь есть исправление.

Я попробовал direnv, как предлагали другие, но нашел его слишком самоуверенным и не совсем то, что я хотел.

Приведенное ниже решение предназначено только для пользователей рыбных раковин . Кроме того, он предполагаетpyproject.tomlфайл и стандартная библиотекаvenv, но это можно легко изменить.

Оболочка fish имеет концепцию обработчиков событий , поэтому вы можете легко определить функцию, которая запускается всякий раз, когда запускается «событие» (сигналы Unix, изменение переменных среды и т. д.). См . этот пост в блоге для краткого ознакомления.

Добавьте следующее в свой~/.config/fish/config.fish(и изменить при необходимости):

      function venv_activate --on-variable PWD
    if test ! -e pyproject.toml
        if test -n "$VIRTUAL_ENV"
            deactivate
        end
        return
    end

    # echo "Found pyproject.toml"
    if test ! -d .venv
        # echo "Creating $(python -V) venv"
        python -m venv .venv --prompt (basename (pwd))
    end

    source .venv/bin/activate.fish
end

В качестве примечания вышеизложенное прекрасно работает сpyenvх.python-versionфайлы также.

Предупреждение о саморекламе!

Если вы используете oh-my-zsh, я разветвил и значительно улучшил (по моему мнению) плагин, который работает как для bash, так и для zsh, и включил инструкции по установке с помощью oh-my-zsh.

https://github.com/RobertDeRose/virtualenv-autodetect

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

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

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

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

function cd() {
  builtin cd "$@"

  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
}

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

Вот альтернатива, которая устанавливает переменную env при переходе в каталог virtualenv и проверяет, является ли активный подкаталог дочерним каталогом указанного виртуального окружения.

Это также деактивируется при выходе или переходе в другой каталог.

Это предполагает, что вы используете Pyenv для управления установками Python, однако вы можете изменитьобозначение чего-либо еще.

      function cd() {

  builtin cd "$@"

    if [[ -f ./pyvenv.cfg ]] ; then
        export VENV_CURRENT=$PWD
        source ./bin/activate
    else
        if [[ $(env | fgrep VENV_CURRENT) ]]; then
            if ! [[ $(pwd | fgrep $VENV_CURRENT) ]]; then
                unset VENV_CURRENT
                deactivate
            fi
        fi
    fi
}

Вы можете использовать функцию ловушки zsh, которая запускается всякий раз, когда вы меняете каталоги. Например, вы можете добавить это в свой файл ~/.zshrc:

      # Define a function to activate/deactivate virtualenvs based on the current directory
function venv_cd() {
  # Check if the current directory has a venv subdirectory
  if [[ -d venv ]]; then
    # If yes, activate it if it's not already active
    if [[ "$VIRTUAL_ENV" != "$PWD/venv" ]]; then
      source venv/bin/activate
    fi
  else
    # If no, deactivate the current virtualenv if any
    if [[ -n "$VIRTUAL_ENV" ]]; then
      deactivate
    fi
  fi
}

Добавьте функцию в хук chpwd, который запускается после каждого cd.

      add-zsh-hook chpwd venv_cd

При желании запустите функцию один раз в начале сеанса.

      venv_cd

Это должно автоматически активировать virtualenv в текущем каталоге, если он существует, или деактивировать его, если вы перейдете в каталог без такового.

Я использовал direnv в прошлом, как упоминали другие. Lyft использует активатор именно для этого сценария .

После создания venv его необходимо активировать (добавить в $PATH). Мы используем активатор, чтобы автоматически активировать venv каждый раз, когда пользователь входит в каталог службы, и деактивировать его при выходе.

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

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

В моем bash_aliases:

function cd() {
    builtin cd "$@"

    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
}

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

  1. Если VIRTUAL_ENV не установлен, тогда:
    1. Проверьте, находимся ли мы в виртуальном окружении
    2. Если да, то активируйте его
  2. Иначе (определено VIRTUAL_ENV), убедитесь, что текущая папка начинается с $VIRTUAL_ENV (удаление /venv часть) и убедитесь, что команда деактивировать существует
    1. Деактивировать среду

Это сценарий:

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
}

Примечание: вам нужно добавить это в.bashrc. Если не работает, проверьте, есть ли у вас.profile не отменяет вашу команду (это случилось со мной)

На основе решения @MS_:

function cd() {
  builtin cd "$@"

  ## If env folder is found then activate the vitualenv
  if [[ -d ./venv ]] ; then
    source ./venv/bin/activate
  fi

  if [[ -n "$VIRTUAL_ENV" ]] ; then
    ## 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
}

pyenv-virtualenv+

      $ pyenv virtualenv 3.10.9 my-project
$ pyenv local my-project

Вот это.

Каждый раз, когда вы переходите в этот каталог проектаpyenvавтоматически:

  • проверяет.python-versionфайл, созданныйpyenv local
  • активирует виртуальную среду из$HOME/.pyenv/versions/3.10.9/envs/my-project

Ссылка

Ответ с наибольшим количеством голосов мне не помог. Но добавив это в свой.bashrcделал

      # Automatically activate Python venv if it exists
auto_activate_venv() {
    if [ -e ".venv/bin/activate" ]; then
        source .venv/bin/activate
    elif [ "$VIRTUAL_ENV" != "" ] && [ ! -e "$PWD/.venv" ]; then
        deactivate
    fi
}

# Override the 'cd' command to call our function
cd() {
    builtin cd "$@" && auto_activate_venv
}

# If you use pushd/popd, you can override them too.
pushd() {
    builtin pushd "$@" && auto_activate_venv
}

popd() {
    builtin popd "$@" && auto_activate_venv
}

Вам не нужно выполнятьdeactivateв каталоге, где существует venv. Когда виртуальная среда активна, вы можете деактивировать ее в любом месте.

Итак, скажем, у вас есть 2 venvs,<somepath>/project1/venvи<somepath>/project2/venv, и в настоящее времяproject1/venvактивен. Если вы хотите переключитьсяproject2/venv, сделайте ниже.

      cd project2
deactivate && source ./venv/bin/activate

Он деактивирует предыдущий и активирует текущий.

Теперь выше вы можете просто создать псевдоним или функцию оболочки в~/.zshrcкак показано ниже:

      function avenv(){
  deactivate
  source ./venv/bin/activate 
}

иди по пути где хочешь активировать venv и просто бегиavenv.

Python venv имеет функцию под названием --prompt, при создании venv вы можете указать приглашение, чтобы в терминале вы понимали, какой venv активен.

      python3 -m venv venv --prompt PROJECT1_VENV

Теперь в терминале это будет выглядеть как(PROJECT1_VENV) -> ~

Другие вопросы по тегам