Direnv: Option to put virtualenv somewhere other than ./.direnv/python-$version

Created on 31 Jul 2017  路  8Comments  路  Source: direnv/direnv

I'd love to be able to control the path to virtualenv direnv creates/activates. This would allow you to:

  • put all your virtualenvs in a single directory (which can be more easily excluded from Time backup systems that don't support exclusion by pattern, e.g. Time Machine)
  • name the virtualenv something more meaningful than the python version (e.g. the project), so the shell prompt is more useful.

From a usability perspective, I'm imagining something like:

use python 3.6.2

would continue to put it in the default path, i.e. ./.direnv/python-3.6.1, but an optional directive, e.g.

use python 3.6.2
virtualenvpath /var/venvs
virtualenvname my_project

would create a virtualenv my_project in /var/venvs if it didn't exist, and activate it there if it did. It would be nice if virtualenvpath could be set globally (and overided locally).

I'm happy to have a go at creating a PR for this, but right now the path seems pretty hard-coded in stdlib.sh. Has anyone tried? Would similar feature be useful for the other isolated environments dotenv manages (rbenv, etc.)?

Feature

Most helpful comment

What I recommend for experimentation is to create your own layout_python function in ~/.config/direnv/direnvrc. Copy and paste the original function and change what you want, that will override it everywhere.

Potentially we could define a layout_python_path='$PWD/.direnv/python-$python_version' and then eval it in export VIRTUAL_ENV=$(eval "$layout_python_path"). That would minimize the changes to the ~/.config/direnv/direnvrc to just setting the layout_python_path variable.

All 8 comments

What I recommend for experimentation is to create your own layout_python function in ~/.config/direnv/direnvrc. Copy and paste the original function and change what you want, that will override it everywhere.

Potentially we could define a layout_python_path='$PWD/.direnv/python-$python_version' and then eval it in export VIRTUAL_ENV=$(eval "$layout_python_path"). That would minimize the changes to the ~/.config/direnv/direnvrc to just setting the layout_python_path variable.

Thanks! The best I could come up with was

layout_python() {
  local python=${1:-python}
  [[ $# -gt 0 ]] && shift
  local old_env=$PWD/.direnv/virtualenv
  unset PYTHONHOME
  if [[ -d $old_env && $python = python ]]; then
    export VIRTUAL_ENV=$old_env
  else
    if [ -z ${layout_python_path+x} ] ; then
        local python_version
        python_version=$("$python" -c "import platform as p;print(p.python_version())")
        if [[ -z $python_version ]]; then
          log_error "Could not find python's version"
          return 1
        fi
        export VIRTUAL_ENV=$PWD/.direnv/python-$python_version
    else
        export VIRTUAL_ENV=${layout_python_path}/${PWD##*/}
    fi
    if [[ ! -d $VIRTUAL_ENV ]]; then
      "$python" -m virtualenv "$@" "$VIRTUAL_ENV"
    fi
  fi
  PATH_add "$VIRTUAL_ENV/bin"
}

It's not pretty, but it works, at least for me.

If the user sets layout_python_path=~/.ves in the .envrc for a project /path/to/my_project (or in the global direnvrc) then the virtualenv is created in ~/.ves/my_project. If they don't set layout_python_path then the virtualenv is created in the current location, i.e. $PWD/.direnv/python-$python_version.

The reason this is kind of ugly is I need to name the virtualenv differently depending on whether layout_python_path is set, in order for it to do what I want _and_ preserve the default behaviour when layout_python_path is not set.

Any suggestions for improvements?

One thing I'm thinking is that if you want to move the .direnv/virtualenv out of the project's folder then this will also apply to the other layouts. So potentially we could introduce a direnv_layout_dir=${direnv_layout_dir:-$PWD/.direnv}. Then in layout_python, export VIRTUAL_ENV=${direnv_layout_dir}/python-$python_version.

Then as a user, you could set whatever logic in your direnvrc. For example direnv_layout_dir=$HOME/.direnv-layouts/$(echo -n $PWD | shasum | awk '{ print $1 }')

With that method the virtualenv will be "named" (i.e. in a path whose last directory is) python-$python_version whatever the user does.

That's not my preference, because (like many many people) I put the name of the virtualenv in $PS1, and I'm used to that name acting as confirmation that I've activated the right one. If they are all called the same thing, e.g. python-3.6.1, it doesn't help.

Hence my hacky if-then-else solution above.

But it's not the end of the world, and I'm happy to go with your approach, rather than have a local fork of direnv!

I'm at a bit of a loss for how to integrate it to the other layouts (my knowledge of those languages and their conventions is extremely limited), but I'll take a look.

That's not my preference, because (like many many people) I put the name of the virtualenv in $PS1, and I'm used to that name acting as confirmation that I've activated the right one. If they are all called the same thing, e.g. python-3.6.1, it doesn't help.

That's true, but direnv can give you a guarantee that the virtualenv loaded is the one for the project. In that case is it still necessary to display which venv has been loaded?

Fair point!

I can do a direnv_layout_dir PR for Python. Would that be useful or do you need one that works for other languages/systems too? If the latter, I'll have to bow out at this point as my knowledge of the equivalents of virtualenv in other languages is not enough.

@williamsmj I went ahead and implemented it in #290. Do you mind giving it a try and give me feedback on the feature?

Feature added in #290. Closing.

Was this page helpful?
0 / 5 - 0 ratings