【问题标题】:How to automatically activate virtualenvs when cd'ing into a directorycd进入目录时如何自动激活virtualenvs
【发布时间】:2017-12-26 06:23:09
【问题描述】:

我的~/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 操作。

  • 我对@9​​87654321@ 很熟悉,在我看来这有点过分。据我所知,它似乎将您所有的 virtualenvs 移到了其他地方,并且增加了比它删除的复杂性更多的复杂性。 (欢迎提出不同意见!)

  • 我对 shell 脚本不太熟悉。如果您可以推荐一个低维护脚本添加到我的~/.zshrc 来完成此操作,那将绰绰有余,但通过一些快速的谷歌搜索,我还没有找到这样的脚本。

  • 我是zsh/oh-my-zsh 用户。 oh-my-zsh 似乎没有这个插件。这个问题的最佳答案是有人贡献了一个oh-my-zsh 插件来做到这一点。 (如果这里的答案乏善可陈,我可能会这样做。

【问题讨论】:

    标签: virtualenv zsh oh-my-zsh virtualenvwrapper direnv


    【解决方案1】:

    在你的 .zshrc 中加入类似的东西

    function cd() {
      if [[ -d ./venv ]] ; then
        deactivate
      fi
    
      builtin cd $1
    
      if [[ -d ./venv ]] ; then
        . ./venv/bin/activate
      fi
    }
    

    编辑:如 cmets cd 中所述,进入当前虚拟环境的子文件夹会停用它。一种想法可能是仅在 cd-ing 进入新环境时停用当前环境,例如

    function cd() {
      builtin cd $1
    
      if [[ -n "$VIRTUAL_ENV" && -d ./venv ]] ; then
        deactivate
        . ./venv/bin/activate
      fi
    }
    

    这仍然可以改进,也许将其变成“提示命令”或文件夹名称上的attempting some prefix matching,以检查路径上某处是否存在虚拟环境,但我的 shell-fu 不够好。

    【讨论】:

    • 嗨@agnul。感谢您的回答!为什么是. ./venv/bin/activate 而不是source venv/bin/activate/
    • "。"相当于“source”,至少对于 zsh 和 bash
    • [[ -d ./venv ]] 仅在当前目录是项目的根目录时才有效。更好地测试环境变量:[ -n "$VIRTUAL_ENV" ].
    • 正如 phd 指出的那样,如果我 cd 进入带有 virtualenv 的项目的子目录,我就会退出 virtualenv。但是,@phd 的解决方案根本不适合我。你们当中有人愿意为此提供帮助吗?当我在上面任何地方都有 virtualenv 的目录中时,如何留在 virtualenv 中?
    • 仍在寻找这个问题的答案!
    【解决方案2】:

    在您的 .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 的答案。

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

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

    【讨论】:

    • 刚刚看到如果您在使用 venv 的项目中并执行 cd ../other_proj,这不起作用
    • @aivision2020 澄清一下:两个项目中的虚拟环境文件夹的名称都是.env吗?此代码假定虚拟环境文件夹的文件夹名称为 .env
    • @boliva 感谢您指出这一点。我已经相应地更新了代码。
    • @Gauthier 只需抓住ifelse(不包括)之间的第一个块,并将其放在倒数第二个fi 之后。
    • @Ari 是的,我将其表述为一个问题,但它更像是一个评论。看看我自己的答案。
    【解决方案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
    

    现在你应该在你的 virtualenv 中了。您可以通过运行pip freeze 进行测试,以查看您的 virtualenv 特定软件包是否已安装。停用

    $ cd ..
    direnv: unloading
    

    【讨论】:

      【解决方案4】:

      这是我的解决方案:

      • 检查是否已经在当前活动的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
      }
      

      【讨论】:

      • 嗨@Gauthier。此代码仅在您进入目标虚拟环境的根目录时才有效。但是,如果你 cd 进入目标虚拟环境的任何子目录,它仍然会保持旧的虚拟环境处于活动状态。
      • @MS_ 没错,这不是我需要的。我想一直 cd 到 / 或者直到你找到一个 dir venv 不会太难?我考虑过,但只有在需要时才会这样做。不仅如此,它对目录的名称寄予厚望,而它应该真正尝试变得更聪明,并通过检查目录的内容来检测目录是否是 virtualenv 环境。但是您的回答也没有做任何事情,我错了吗?
      • 进入非项目目录时不会停用(不一定重要,但我认为这样做很好)。
      【解决方案5】:

      为了后代:我使用了@MS_ 的解决方案,但遇到了cding 直接从一个项目到另一个项目的问题,它会停用旧的 virtualenv,但不会激活新的 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
      }
      

      【讨论】:

        【解决方案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 "$@"
          
            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
          }
          

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

          【讨论】:

            【解决方案8】:

            这是我的解决方案:

            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 是否没有覆盖您的命令(它发生在我身上)

            【讨论】:

            • 这不支持从一个 venv 目录移动到另一个
            【解决方案9】:

            基于@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
            }
            

            【讨论】:

              【解决方案10】:

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

              例如:

              plugins=(
                git pip python brew virtualenvwrapper
              )
              

              【讨论】:

                【解决方案11】:

                如果不是direnv,你应该尝试类似autoenv

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

                无论如何,两者都在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
                

                "如果一个目录包含.env文件,当你cd进入它时会自动执行。启用时(设置AUTOENV_ENABLE_LEAVE为非空字符串),如果目录包含.env.leave文件,离开时会自动执行。”

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

                【讨论】:

                • 感谢您不仅推荐了一个库,还包括解释和命令行语法。这是 Stack Overflow 上的最佳实践,但许多新贡献者未能遵守。
                • 我非常同意你的观点(不过,这是我的第一个答案!),再加上一点解释,这与@mc_kaiser 已经指出的类似。
                【解决方案12】:

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

                这是对daveruinseverything's answer 的改进,这是对MS_'s answer 的改进。

                我们正在使用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
                }
                

                【讨论】:

                • 如果您能根据您的回答评估我创建的插件的功能,那就太好了。更多信息here。欢迎请求请求!
                • @DanielKaminskideSouza 我很高兴有人发现代码足够有用,可以用它制作插件。我感觉很好。非常感谢。
                • 您的研究导致我相信迄今为止最好的解决方案。该插件是您研究的延续......我刚刚添加了一个可以使用任何环境名称的功能并进行了一些速度优化。
                【解决方案13】:

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

                【讨论】:

                  【解决方案14】:

                  对于使用(或考虑使用)pyenv 的任何人,可以使用pyenv-virtualenv 插件非常方便地实现这一点,如here 所述。

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

                  【讨论】:

                    【解决方案15】:

                    类似于Jake's answer,但支持cd从一个virtualenv 到另一个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
                    }
                    

                    【讨论】:

                      【解决方案16】:

                      正如其他人所提到的,我过去曾使用过direnv。 Lyft 使用 aactivator 这个exact scenario

                      一旦构建了 venv,它就必须被激活(添加到 $PATH)。我们使用 aactivator 来在用户每次进入服务目录时自动激活 venv,并在他们离开时停用。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2022-10-09
                        • 1970-01-01
                        • 2017-05-30
                        • 2022-07-03
                        • 2012-01-03
                        • 2012-04-20
                        相关资源
                        最近更新 更多