direnv
Posted on Sat 04 May 2019 in linux
What is direnv?
direnv is a simple environment switcher for the shell that allows to load/unload environment variables depending on the directory the user is changing into. It supports various shells, including bash, zsh, tcsh, fish shell and elvish.
In the following, its basic functionality will be explained using the combination of bash and python.
Installation
Simply install direnv
from the Debian repository:
sudo apt install direnv
To activate direnv
for the bash, simply add the following line to the
~/.bashrc
file:
eval "$(direnv hook bash)"
Make sure to reload the file again:
source ~/.bashrc
Basic Idea
In each project directory, which should be managed by direnv
, place a
corresponding .envrc
file with the corresponding configuration.
For example, a few environment variables should be set, when entering the
foo/
directory. Thus, a .envrc
file in the foo/
directory is placed with
the following content:
export PATH=$PWD/example/bin:$PATH
export TMPDIR=$PWD/tmp
When saving the .envrc
file, the following warning will appear:
direnv: error .envrc is blocked. Run
direnv allow
to approve its content.
This is simply a security mechanism that avoids the automatic loading of malicious content, e.g. when checking out a foreign git repository.
To activate the .envrc
file simply type:
direnv allow
After allowing the access to the file, direnv
will automatically set the
previously defined environment variables. The same will happen each time you
will enter the foo/
directory. In contrast, when leaving the directory, the
environment variables will be unset again.
Python
In the next step, the more sophisticated case of setting a Python virtual
environment is discussed. There are many ways to setup a virtual environment
for python, each requiring a special direnv
setup. Here, the built-in venv
module is used that does not need any additional software.
To this end, add the following new layout to the general direnv
user
configuration file ~/.direnvrc
:
realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
layout_python-venv() {
local python=${1:-python3}
[[ $# -gt 0 ]] && shift
unset PYTHONHOME
if [[ -n $VIRTUAL_ENV ]]; then
VIRTUAL_ENV=$(realpath "${VIRTUAL_ENV}")
else
local python_version
python_version=$("$python" -c "import platform; print(platform.python_version())")
if [[ -z $python_version ]]; then
log_error "Could not detect Python version"
return 1
fi
VIRTUAL_ENV=$PWD/.direnv/python-venv-$python_version
fi
export VIRTUAL_ENV
if [[ ! -d $VIRTUAL_ENV ]]; then
log_status "no venv found; creating $VIRTUAL_ENV"
"$python" -m venv "$VIRTUAL_ENV"
fi
PATH_add "$VIRTUAL_ENV/bin"
}
Subsequently, add the python-venv
new layout to the .envrc
file of the
Python project:
export VIRTUAL_ENV=env
layout python-venv
After allowing the access to the .envrc
file, direnv
will automatically
change to the virtual environment in env/
(or create it, if it is not
existing yet).
In contrast, to normally changing into the virtual environment there is
no prompt indicator (env)
that is highlighting the change. To enable this
behavior for direnv
, add the following to ~/.bashrc
:
show_virtual_env() {
if [[ -n "$VIRTUAL_ENV" && -n "$DIRENV_DIR" ]]; then
echo "($(basename $VIRTUAL_ENV))"
fi
}
export -f show_virtual_env
PS1='$(show_virtual_env)'$PS1
Then, reload the file again:
source ~/.bashrc
After that, the familiar (env) prompt will appear, when changing into
virtual environments with direnv
.
For other ways to make use of direnv
in combination with Python
see the official wiki.
Of course, similar setups can be created for other programming languages.