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

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

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

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

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

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

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


person Alex Lenail    schedule 20.07.2017    source источник


Ответы (14)


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

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

  builtin cd $1

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

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

function cd() {
  builtin cd $1

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

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

person agnul    schedule 20.07.2017
comment
Привет @agnul. Спасибо за этот ответ! Почему . ./venv/bin/activate вместо source venv/bin/activate/? - person Alex Lenail; 20.07.2017
comment
. эквивалентен исходному коду, по крайней мере, для zsh и bash - person agnul; 20.07.2017
comment
[[ -d ./venv ]] работает, только если текущий каталог является корнем проекта. Лучший тест для env var: [ -n "$VIRTUAL_ENV" ]. - person phd; 20.07.2017
comment
Как указывает phd, если я перехожу в подкаталог проекта с virtualenv, я выхожу из virtualenv. Однако решение @phd у меня вообще не работает. Кто-нибудь из вас готов помочь с этим? Как мне оставаться в virtualenv, когда я нахожусь в каталоге с virtualenv где-то выше? - person Alex Lenail; 21.08.2017
comment
Все еще ищу ответ на этот вопрос! - person Alex Lenail; 08.09.2017
comment
есть ли вообще смысл запускать deactivate? например если вы выйдете из оболочки с помощью ctr+d, сеанс не будет автоматически закрыт? - person ccpizza; 07.05.2019
comment
Я думаю, что это должно быть: function cd() { builtin cd $1 if [[ -d venv ]] ; then if [[ -n "$VIRTUAL_ENV" ]] ; then deactivate fi source venv/bin/activate fi } - person Mohit Chawla; 20.08.2019
comment
Это также не будет переключать среды, когда вы переходите из одной среды в другую; он только деактивирует новый, но не активирует новый. - person Robert; 08.04.2020
comment
@AlexLenail это потому, что вы не можете исправить это в пару строк в вашем .zshrc, но вам нужно что-то еще ... посмотрите на autoenv и direnv, которые я предоставил в своем ответе ниже;) - person andreagalle; 01.07.2020

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

Этот код не деактивирует виртуальную среду, даже если кто-то войдет в подпапку. На основе ответов @agnul и @Жиль.

Если virtualenv создан pipenv, рассмотрите эту вики-страницу.

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

person MS_    schedule 13.06.2018
comment
только что увидел, что это не работает, если вы в проекте с venv и делаете cd ../other_proj - person aivision2020; 17.02.2019
comment
@aivision2020 aivision2020 Для уточнения: имена папок виртуальной среды в обоих проектах .env? Этот код предполагает, что имя папки для папки виртуальной среды будет .env. - person MS_; 18.02.2019
comment
Я полагаю, что вы имели в виду ./env вместо .env в своем предыдущем комментарии, за исключением того, что ваше решение — именно то, что мне нужно. - person boliva; 18.02.2019
comment
@boliva Я имею в виду папку ./.env, то есть папку .env в текущей папке проекта Python. Таким образом, ваша текущая папка virtualenv проекта должна быть скрытой папкой с именем .env. Надеюсь, это поможет. - person MS_; 19.02.2019
comment
правильно, но ваш фрагмент кода ссылается на ./env (хотя не буду педантичным, я просто указывал на это для ясности и во избежание путаницы между опубликованным кодом и вашим комментарием) - person boliva; 20.02.2019
comment
@boliva Спасибо, что указали на это. Я обновил код соответственно. - person MS_; 20.02.2019
comment
обратите внимание, что этот фрагмент работает для виртуальных папок с именем .env. Если вы назвали свою папку любым другим именем, измените его в строке, которая говорит source ./.env/bin/activate. Имя папки — это то, что вы указали при запуске mkvirtualenv venv -p python3. Вы также можете ls указать свой текущий каталог, чтобы узнать, сможете ли вы узнать, как вы его назвали. - person Alex H; 23.03.2019
comment
Мне кажется, что переход из каталога с venv в другой каталог с venv будет деактивировать, а затем ничего? Я ожидаю, что скрипт деактивирует старый и активирует новый. - person Gauthier; 24.04.2019
comment
@Gauthier просто возьмите первый блок между if и else (не включительно) и поместите его после предпоследнего fi. - person Ari; 02.05.2019
comment
@ Ари Да, я сформулировал это как вопрос, но это был скорее комментарий. Проверьте мой собственный ответ. - person Gauthier; 02.05.2019
comment
Вам следует рассмотреть возможность использования функции chpwd вместо cd. Это будет работать и с AUTOCD. - person Marcel; 04.09.2020
comment
Я создал плагин для zsh, следуя вашим рассуждениям, @MS_. См. здесь: github.com/DanielAtKrypton/viper-env - person Daniel Kaminski de Souza; 31.01.2021

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

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

$ cd ..
direnv: unloading
person mc_kaiser    schedule 10.09.2018

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

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

Во всяком случае, оба были протестированы на 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 в него войдете. ."

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

person andreagalle    schedule 18.05.2020
comment
Спасибо, что не просто порекомендовали библиотеку, но и добавили как объяснение, так и синтаксис командной строки. Это лучшая практика для Stack Overflow, но многие новые участники не соблюдают ее. - person Jeremy Caney; 18.05.2020
comment
Я полностью согласен с вами (хотя это был мой самый первый ответ!), С небольшим количеством пояснений, это что-то похожее на то, что уже указал @mc_kaiser. - person andreagalle; 18.05.2020

Для потомков: я использовал решение @MS_, но столкнулся с проблемой, когда cding напрямую из одного проекта в другой деактивирует старый 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 "$@"

  ## 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
}
person daveruinseverything    schedule 25.05.2019

это решение без 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")
person yattara    schedule 15.08.2019

намного самый простой вариант (в 2019+) — это добавить virtualenvwrapper в свой ~/.zshrc plugins

Например:

plugins=(
  git pip python brew virtualenvwrapper
)
person Roman    schedule 13.02.2020

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

Это улучшение по сравнению с daveruinseverything answer, которое является улучшением по сравнению с ответ 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
}
person blueray    schedule 18.09.2020
comment
было бы здорово, если бы вы могли оценить функциональность плагина, который я создал на основе вашего ответа. Подробнее здесь. Пулл-реквесты приветствуются! - person Daniel Kaminski de Souza; 01.02.2021
comment
@DanielKaminskideSouza Я рад, что кто-то нашел код достаточно полезным, чтобы сделать из него плагин. Я чувствую себя очень хорошо. Спасибо большое. - person blueray; 01.02.2021
comment
ваше исследование привело к тому, что я считаю лучшим решением на данный момент. Плагин является продолжением вашего исследования... Я просто добавил возможность работать с любым именем среды и сделал некоторые оптимизации скорости. - person Daniel Kaminski de Souza; 02.02.2021

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

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

person hummat    schedule 18.03.2021

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

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

In my 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
}
person Gauthier    schedule 24.04.2019
comment
Привет @Готье. Этот код работает, только если вы входите в корневой каталог целевой виртуальной среды. Однако если вы cd войдете в любой подкаталог целевой виртуальной среды, старая виртуальная среда останется активной. - person MS_; 03.05.2019
comment
@MS_ это правильно, мне это не нужно. Я думаю, было бы не слишком сложно перезагружаться до / или до тех пор, пока вы не найдете каталог venv? Я думал об этом, но сделаю это только тогда, когда у меня будет необходимость. Более того, он возлагает определенные надежды на имя каталога, тогда как на самом деле он должен быть умнее и определять, является ли каталог средой virtualenv, проверяя его содержимое. Но ваш ответ тоже ничего из этого не делает, я ошибаюсь? - person Gauthier; 03.05.2019
comment
Это не деактивируется при входе в каталог, не относящийся к проекту (не обязательно важно, но я думаю, что это приятно). - person Jonny Shanahan; 15.01.2020

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

Это будет работать для любого имени или каталога виртуальной среды (не только ./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
}

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

person Jake Tesler    schedule 10.10.2019

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

  1. If VIRTUAL_ENV is not set then:
    1. Check if we're inside a virtual env
    2. Если да, то активируйте его
  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

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

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 вашу команду (это случилось со мной)

person gfournier    schedule 21.12.2019
comment
Это не поддерживает переход из одного каталога venv в другой. - person Jonny Shanahan; 15.01.2020

На основе решения @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
}
person Jonny Shanahan    schedule 15.01.2020

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

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

person akkk3    schedule 02.02.2021