web-dev-qa-db-fra.com

Comment activer automatiquement virtualenvs lors du cd'ing dans un répertoire

J'ai un tas de projets dans mon ~/Documents. Je travaille presque exclusivement en python, donc ce sont essentiellement tous les projets python. Chacun, par exemple ~/Documents/foo a son propre virtualenv, ~/Documents/foo/venv (on les appelle toujours venv). Chaque fois que je passe d'un projet à l'autre, soit environ 10 fois par jour, je fais

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

J'ai atteint le point où j'en ai marre de taper deactivate et source venv/bin/activate. Je cherche un moyen de simplement cd ../foo et faire gérer les opérations virtualenv pour moi.

  • Je connais VirtualEnvWrapper qui est un peu lourd à mon avis. Il semble déplacer tous vos virtualenvs ailleurs, et ajoute un peu plus de complexité qu'il n'en supprime, pour autant que je sache. (Les opinions dissidentes sont les bienvenues!)

  • Je ne connais pas trop bien les scripts Shell. Si vous pouvez recommander un script à faible maintenance à ajouter à mon ~/.zshrc qui accomplit cela, ce serait plus que suffisant, mais à partir d'une recherche rapide sur Google, je n'ai pas trouvé un tel script.

  • Je suis zsh/ oh-my-zsh utilisateur. oh-my-zsh ne semble pas avoir de plugin pour cela. La meilleure réponse à cette question serait que quelqu'un contribue à oh-my-zsh plugin qui fait cela. (Ce que je pourrais faire si les réponses ici sont ternes.

18
Alex Lenail

Mettez quelque chose comme ça dans votre .zshrc

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

  builtin cd $1

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

Edit : Comme indiqué dans les commentaires cd- dans un sous-dossier de l'environnement virtuel actuel le désactiverait. Une idée pourrait être de désactiver l'environnement actuel uniquement si cd- en un nouveau, comme

function cd() {
  builtin cd $1

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

cela pourrait encore être amélioré, peut-être en le transformant en une "commande d'invite" ou en essayant une correspondance de préfixe sur les noms de dossier pour vérifier qu'il y a un env virtuel quelque part sur le chemin, mais mon Shell-fu n'est pas bon assez.

7
agnul

Ajoutez les éléments suivants dans votre .bashrc ou .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
}

Ce code ne désactivera pas le virtualenv même si quelqu'un va dans le sous-dossier. Inspiré par les réponses de @ agnul et @ Gilles .

Si le virtualenv est fait par pipenv, alors veuillez considérer cette page wiki .

De plus, pour plus de sécurité, veuillez considérer direnv .

14
MS_

Plutôt que d'écrire un script personnalisé, vous pourriez utiliser direnv . Ce n'est pas une solution spécifique à zsh (pour cela, vous pouvez essayer zsh-autoenv ), mais elle est bien entretenue et facile à utiliser avec zsh. Une fois que vous l'avez installé, vous voudrez mettre eval "$(direnv hook zsh)" à la fin de votre .zshrc. À ce stade, vous pouvez faire:

$ 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

Vous devriez maintenant être dans votre virtualenv. Vous pouvez tester en exécutant pip freeze pour voir que vos packages spécifiques à virtualenv sont installés. Pour désactiver

$ cd ..
direnv: unloading
5
mc_kaiser

Pour la postérité: j'ai utilisé la solution de @ MS_ mais j'ai rencontré le problème où cding directement d'un projet à un autre désactive l'ancien virtualenv mais n'active pas le nouveau. Il s'agit d'une version légèrement modifiée de cette solution qui résout ce problème:

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

Par loin l'option la plus simple (en 2019+) est d'ajouter virtualenvwrapper dans votre ~/.zshrcplugins

Par exemple:

plugins=(
  git pip python brew virtualenvwrapper
)
1
Roman

Voici ma solution, qui:

  • vérifie si déjà dans le venv actuellement actif, et ne fait rien dans ce cas
  • s'il y a un dossier venv, désactivez celui qui est actif s'il y en a un
  • activer le nouveau venv peu importe s'il y en avait un ancien ou non.

Dans mon 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
}
0
Gauthier

Basé sur la solution de @ 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
}
0
Jonny Shanahan

Voici (encore) une autre solution pour activer automatiquement un environnement virtuel; il est basé sur un certain nombre de réponses déjà publiées ici.

Cela fonctionnera pour tout nom ou répertoire de l'environnement virtuel (pas seulement ./env, ./venv, etc.). Prend également en charge les sous-répertoires, ainsi que cd- dans les liens symboliques des dossiers de l'environnement virtuel (parent).

Ce code recherche un pyvenv.cfg fichier au lieu d'un répertoire nommé particulier. Si l'un se trouve dans un sous-répertoire du dossier actuel, l'environnement est automatiquement activé. Une fois à l'intérieur d'un environnement virtuel, cet état est conservé jusqu'à ce que vous quittiez le répertoire de l'environnement virtuel parent, auquel point l'environnement est désactivé.

Placez-le dans votre .bashrc ou .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
}

Personnellement, je pense que c'est une amélioration de beaucoup de solutions ici, car cela devrait fonctionner pour n'importe quel environnement virtuel

0
Jake Tesler

c'est la solution sans cd'ing, avec zsh réglé sur setop auto_cd nous pourrons changer de répertoire sans cd, tapez simplement le nom du répertoire et appuyez sur Entrée. c'est une des solutions ci-dessus:

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

Voici ma solution:

  1. Si VIRTUAL_ENV n'est pas défini, alors:
    1. Vérifiez si nous sommes dans un environnement virtuel
    2. Si oui, activez-le
  2. Sinon (VIRTUAL_ENV est défini), vérifiez que le dossier actuel commence par $ VIRTUAL_ENV (en supprimant le /venv part) et vérifiez que la commande de désactivation existe
    1. Désactiver l'environnement

Voici le script:

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
}

Remarque: vous devez l'ajouter à .bashrc. Si cela ne fonctionne pas, vérifiez si votre .profile ne remplace pas votre commande (cela m'est arrivé)

0
gfournier