# 콘다 (conda) + direnv

많은 프로젝트들이 디렉토리 단위로 파일과 의존성을 관리합니다. direnv는 디렉토리 단위로 각종 환경 변수를 자동으로 변경해주는 기특한 녀석입니다. 사용법도 간단하고, 효과도 좋습니다. 가상환경을 많이 사용하는 파이선(Python)은 물론 설정이 좀 딱딱한 느낌의 고(Go) 프로그래밍 언어와도 잘 어울립니다.

# 1. direnv

direnv 사이트에 들어가보면 간단하고 충분한 사용 예가 나옵니다. 쭉 따라가며 주석으로 설명해보겠습니다.

# 프로젝트 디렉토리로 이동
$ cd ~/my_project
# FOO 라는 변수가 선언되지 않은 것을 확인
$ echo ${FOO-nope}
nope
# FOO 변수의 값을 foo 로 선언하고 .envrc 파일에 저장.
# .envrc 파일 내용이 적용되지 않음 메시지 표시됨
$ echo export FOO=foo > .envrc
.envrc is not allowed
# .envrc 파일 내용을 적용하는 명령. direnv allow
$ direnv allow .
direnv: reloading
direnv: loading .envrc
direnv export: +FOO
# FOO 변수의 값이 foo 임을 확인
$ echo ${FOO-nope}
foo
# 프로젝트 디렉토리에서 빠져나옴
$ cd ..
direnv: unloading
direnv export: ~PATH
# FOO 변수가 선언되지 않음을 확인
$ echo ${FOO-nope}
nope

결국 .envrc 파일만 프로젝트에 맞게 설정하면, cd 명령으로 direnv가 시동되어 자연스럽게 원하는 환경으로 바뀝니다. 이제 파이선 가상환경과 어떻게 연동되는지 확인해봅시다.

# 2. 가상환경과 direnv

direnv는 직접 파이선 가상환경을 지원하지 않습니다. 대신 공식 위키에 있는 자세한 설명을 따라하기만 하면 됩니다.

할 일은 세 가지 입니다.

  1. 레이아웃 (layout) 정의
  2. 프로젝트 디렉토리에 환경설정
  3. 쉘 함수 정의

# 2.1 레이아웃 (layout) 정의

$HOME/.direnvrc 파일을 열고 (없으면 생성해서) 아래 내용을 추가합니다. layout_conda 라는 함수를 정의했으니 이제 conda라는 이름의 레이아웃(layout)을 사용할 수 있습니다.

layout_conda() {
  local CONDA_HOME="${HOME}/miniconda3/"
  PATH_add "$CONDA_HOME"/bin

  if [ -n "$1" ]; then
    # Explicit environment name from layout command.
    local env_name="$1"
    source activate ${env_name}
  elif (grep -q name: environment.yml); then
    # Detect environment name from `environment.yml` file in `.envrc` directory
    source activate `grep name: environment.yml | sed -e 's/name: //'`
  else
    (>&2 echo No environment specified);
    exit 1;
  fi;
}

# 2.2 프로젝트 디렉토리에 환경 설정

$PROJECT_HOME/.envrc 파일을 열어서 (없으면 생성해서) 다음 내용을 추가합니다. 여기서는 가상환경의 이름을 hello 라고 했습니다.

layout conda hello

이제 디렉토리를 들어갔다 (cd $PROJECT_HOME) 나왔다 (cd ..) 해봅시다. 그런데, 아무일도 일어나지 않는다면 hook 이 필요합니다.

배시라면 .bashrc 파일에 내용을 넣고 source .bashrc 명령으로 초기 환경설정을 다시 합니다.

eval "$(direnv hook bash)"

Z 쉘이라면 .zshrc 파일에 내용을 넣고 source .zshrc 명령으로 초기 환경설정을 다시 합니다.

eval "$(direnv hook zsh)"

이제 프로젝트 디렉토리에 들어가면 친절한 빨간 경고가 보입니다. direnv allow를 해주는 것으로 끝입니다.

$ cd hello
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.

hello $ direnv allow
direnv: loading .envrc
direnv: export +CONDA_DEFAULT_ENV +CONDA_EXE +CONDA_PREFIX +CONDA_PROMPT_MODIFIER +CONDA_PYTHON_EXE +CONDA_SHLVL -DYLD_LIBRARY_PATH ~PATH

hello $ python -c "import flask; print(flask.__version__)"
1.0.2
hello $ cd ..
direnv: unloading

$ python -c "import flask; print(flask.__version__)"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'flask'

이제 원하는데로 프로젝트 디렉토리에 들어가면 플라스크를 사용할 수 있고 프로젝트를 나오면 플라스크를 사용할 수 없는 것으 ㄹ확인할 수 있습니다. 하지만 뭔가 이상하지 않나요? 그렇습니다. source activate를 할때는 잘 표시되던 프롬프트의 "(가상환경 이름)" 항목이 보이지 않습니다.

# 2.3 쉘 함수 정의

이 문제도 wiki에 잘 설명되어있습니다. 우선 설명된 해결 방법은, 배시 쉘은 $HOME/.bashrc, Z 쉘은 $HOME/.zshrc 파일을 열고 아래 내용을 추가합니다.

# $HOME/.bashrc
show_virtual_env() {
  if [ -n "$VIRTUAL_ENV" ]; then
    echo "($(basename $VIRTUAL_ENV))"
  fi
}
export -f show_virtual_env
PS1='$(show_virtual_env)'$PS1

내용은 단순합니다. $VIRTUAL_ENV 변수가 있으면 프롬프트에 표시하고 없으면 표시하지 않습니다. 그런데 콘다는 다른 변수, $CONDA_DEFAULT_ENV 를 사용합니다. 저는 virtualenv 기반의 pipenv 와 conda 를 둘 다 사용하고 있기 때문에 아래와 같이 조금 수정했습니다, 가상환경 이름도 노란 색으로 표시해봤습니다.

# $HOME/.zshrc
autoload -U colors && colors
show_virtual_env() {
  if [ -n "$VIRTUAL_ENV" ]; then
    echo "($(basename $VIRTUAL_ENV))"
  elif [ -n "$CONDA_DEFAULT_ENV" ]; then
    echo "($(basename $CONDA_DEFAULT_ENV))"
  fi
}
export -f show_virtual_env
PS1='%{$fg[yellow]%}$(show_virtual_env)%{$reset_color%}'$PS1

conda

# 3. 마무리

direnv 도 최고의 툴은 아닙니다. 마음대로, 예상한대로 동작하지 않는 기능도 있습니다. 하지만, 적절하게 사용한다면 여러분의 작업효율에 큰 도움이 되리라 생각합니다.

Last Updated: 3/23/2020, 11:10:33 PM