Python의 모듈 간 의존성 문제를 해결하기 위해서 virtualenv나 venv를 많이 사용하실 겁니다. 시스템에 설치된 모듈들과 virtual environment 내에 설치된 모듈들을 분리해주기 때문에 모듈들이 꼬일 걱정 없이 사용할 수 있어서 참 좋습니다. 개인적으로 저는 새 Python 프로젝트를 시작하면 venv와 git 초기화를 꼭 실행합니다.

그런데 가끔 내부적으로 어떻게 이 기능이 작동하는지 궁금할 때가 있습니다. 그래서 오늘은 venv가 어떻게 작동하는지 한번 알아보겠습니다. (virtualenv도 작동 방식은 거의 동일합니다.)

1. venv를 만들자

먼저 아래 명령으로 virtual environment를 만듭니다.

python3 -m venv env

그럼 현재 디렉터리에 env 라는 디렉터리는 만들어지고, venv가 필요한 내용들을 채워줍니다. env의 내부를 보면 아래와 같습니다.

env
|-- bin
|   |-- activate
|   |-- activate.csh
|   |-- activate.fish
|   |-- easy_install
|   |-- easy_install-3.7
|   |-- pip
|   |-- pip3
|   |-- pip3.7
|   |-- python -> python3
|   `-- python3 -> /usr/local/bin/python3
|-- include
|-- lib
|   `-- python3.7
|       `-- site-packages
|           |-- __pycache__
|           |-- easy_install.py
|           |-- pip
|           |-- pip-19.2.3.dist-info
|           |-- pkg_resources
|           |-- setuptools
|           `-- setuptools-41.2.0.dist-info
`-- pyvenv.cfg

2. activate이 하는 일은 뭘까

보통 작업을 시작할 때 source env/bin/activate을 실행시켜서 virtual environment로 들어갑니다. 그런데 virtual environment로 들어간다는 것이 뭘까요? Virtual Machine과 같은 어렵고 무거운 개념은 물론 아닙니다. activate script가 하는 일은 생각보다(?) 아주 간단합니다.

activate script의 코드 일부를 살펴보겠습니다.

VIRTUAL_ENV="/Users/ceongjeein/Workspace/venv_demo/env"
export VIRTUAL_ENV

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH

기존의 PATH(실행 파일들의 위치) 환경 변수를 _OLD_VIRTUAL_PATH라는 새 환경 변수에 백업을 하고, PATH 앞에다가 현재 venv의 bin 디렉터리를 추가합니다. 이렇게 하면 시스템 상의 명령보다 현재 venv의 bin 디렉터리에 있는 명령들이 먼저 실행이 됩니다. 즉, "python"이라고 실행하면 시스템의 python이 아닌, 현재 venv의 python이 실행이 됩니다.

백업해 둔 _OLD_VIRTUAL_PATH는 아래와 같이 나중에 deactivate 명령 시에 PATH를 원상 복귀하는 데 사용합니다.

if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
        PATH="${_OLD_VIRTUAL_PATH:-}"
        export PATH
        unset _OLD_VIRTUAL_PATH
    fi

이게 전부입니다. Prompt를 약간 손 본다거나, PYTHONHOME 환경 변수를 끈다거나 하는 몇 가지 동작이 있지만 가장 중요한 것은 저것이 전부입니다. venv가 이렇게 간단할 수 있는 이유는 모듈을 로딩하고 관리하는 것 자체는 Python이 담당하기 때문입니다.

위 코드에서도 볼 수 있지만 원리적으로는 사실 source env/bin/activate 명령을 사용하지 않아도 됩니다. Python 코드를 실행할 때 env/bin/python 이라고만 쓰면 똑같거든요.