如何在进入目录时自动激活 virtualenvs

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

我的 ~/Documents 中有很多项目。我几乎只在 python 工作,所以这些基本上都是 python 项目。每一个,例如~/Documents/foo 有自己的 virtualenv,~/Documents/foo/venv(它们总是被称为 venv)。每当我在项目之间切换时,每天大约 10 次,我都会

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

我已经厌倦了输入 deactivatesource venv/bin/activate我正在寻找一种方法 cd ../foo 并为我处理 virtualenv 操作。

在你的 .zshrc 中加入这样的东西

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

  builtin cd 

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

编辑:如评论中所述cd-进入当前虚拟环境的子文件夹将停用它。一个想法可能是仅当 cd 进入新环境时才停用当前环境,例如

function cd() {
  builtin cd 

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

仍然可以改进,也许将其变成文件夹名称上的 "prompt command" 或 attempting some prefix matching 以检查路径上某处是否存在虚拟环境,但我的 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。灵感来自 and @Gilles.

的答案

如果virtualenv是pipenv制作的,请考虑this wiki page

此外,为了增加安全性,请考虑 direnv

您可以使用 direnv. It's not a zsh specific solution (for that you could try 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

这是我的解决方案,其中:

  • 检查是否已经处于当前活动状态 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
}

为了后代:我使用了@MS_ 的解决方案,但 运行 遇到了 cd 直接从一个项目到另一个项目停用旧 virtualenv 但不激活新 virtualenv 的问题。这是解决此问题的解决方案的略微修改版本:

# auto activate virtualenv
# Modified solution based on 
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_cd 将能够在没有 cd 的情况下更改目录,只需键入目录名称并点击进入。 它是上述解决方案的结果:

    # auto activate virtualenv
# Modified solution based on 
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")

这是(又)另一种自动激活虚拟环境的解决方案;它基于此处已发布的许多答案。

这将适用于 任何 虚拟环境名称或目录(不仅仅是 ./env./venv 等)。还支持子目录,以及 cd-ing 到虚拟环境(父)文件夹的符号链接。

此代码搜索 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
}

我个人认为这是对此处许多解决方案的改进,因为它适用于任何虚拟环境

这是我的解决方案:

  1. 如果未设置 VIRTUAL_ENV,则:
    1. 检查我们是否在虚拟环境中
    2. 如果是,则激活它
  2. Else(定义了VIRTUAL_ENV),检查当前文件夹是否以$VIRTUAL_ENV开头(去掉/venv部分)并验证deactivate命令是否存在
    1. 停用环境

这是脚本:

function cd() {
  builtin cd 

  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
}

到目前为止最简单的选择(在 2019 年以后)是将 virtualenvwrapper 添加到您的 ~/.zshrc plugins

例如:

plugins=(
  git pip python brew virtualenvwrapper
)

你应该试试 autoenv if not direnv

第一个被认为是“轻量级”,而第二个“简单,更高质量的软件”,分别听给每个人的作者,谈论另一个人的项目。因此,在我看来,它们都是不错的选择,两者都可以尝试!

无论如何,两者都已经在 zsh shell 上进行了测试。 特别是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

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

查看 https://github.com/inishchith/autoenv 以获得更详细的说明!...

这是一个仅限 zsh 的解决方案。

这是对 which is an improvement over 的改进。

我们正在使用 precmd 挂钩而不是覆盖 cd

我们添加了另一个额外的功能。假设目录结构为

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

如果你现在在 subdir2 中打开新的终端,或者直接从其他地方 cd 到 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
}

对于像我这样的 python 开发人员,我在 cd 进入 python 项目时使用 this plugin 创建、激活 python 虚拟环境,它也会在 cd 进入另一个目录后停用。

对于使用(或考虑使用)pyenv this can be achieved very conveniently using the pyenv-virtualenv plugin as described here 的任何人。

基本上,您只需将 .python-version 文件添加到指定 virtualenv 名称的目录。

类似于但支持cd从一个virtualenv到另一个。在这种情况下,它会停用旧的 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
}

我用过direnv in the past, as others have mentioned. Lyft use aactivator for this exact scenario.

Once the venv is built it must be activated (added to $PATH). We use aactivator to automatically activate the venv each time a user enters the service directory and deactivates as they leave.