pyenv 동작방식 이해를 위한 실험

나는 원래 pyenv ( + pyenv-virtualenv) 를 사용해서 python 개발환경의 dependency 를 관리 해 왔다.
(이걸 여기다 깔았었나 .. virtualenv 에다 깔았었나…)
그러던 참에 poetry 라는걸 알게 되어 써보려고 했는데..
pyenv 도 정확히 이해가 안되는데 여기에 또 저걸 쓰면 과연 잘 할수 있을까? 싶어서, 일단 pyenv 의 동작방식을 명확히 하기 위한 실험을 해 봤다.

혹시 pyenv 의 동작 방식에 대한 나와같은 혼미함을 가진 분이 있다면 정리된 글이 도움이 되길 바라며..

기본 테스트 환경

  • mac catalina 10.15.7
  • pyenv 1.2.20 설치됨 + 각종 python 버전들 이미 설치되어 사용중
  • pyenv 설치 경로 : /Users/joshua/.pyenv

첫번째 테스트 환경 - clean python 상태

  1. pyenv install 3.6.10 으로 python 3.6.10 클린 인스톨
  2. pyenv global 3.6.10 지정
$ python --version
Python 3.6.10

$ which python
/Users/joshua/.pyenv/shims/python

$ pip --version
pip 18.1 from /Users/joshua/.pyenv/versions/3.6.10/lib/python3.6/site-packages/pip (python 3.6)

$ which pip
/Users/joshua/.pyenv/shims/pip

$ pyenv which pip
/Users/joshua/.pyenv/versions/3.6.10/bin/pip

pyenv 의 내부 구조를 좀 아시는분은 알겠지만, 각종 python 관련 명령어들은 shims 아래 디렉토리에서 가져다 쓰게 된다.
shims 디렉토리를 가보면 이전에 각종 python 버전들을 사용하면서 설치했던 binary 명령어들이 다 들어있다.
각 shims 아래의 명령어를 실행해보면, 현재 python 버전에 따라 다르게 동작한다.

다른 python 버전에 깔려있는 jupyter 노트북 명령어를 한번 실행해봤다.

$ jupyter
pyenv: jupyter: command not found

The `jupyter' command exists in these Python versions:
3.6.11
3.6.11/envs/project1
3.6.11/envs/project2
project1
project2

Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.

$ pyenv whence jupyter
3.6.11
3.6.11/envs/project1
3.6.11/envs/project2
project1
project2

오호라~ 현재 python 버전에 깔리지 않은것은 실행이 안되고, 대신 어디 깔려있는지 알려주는구만~
pyenv whence 기능으로 어떤 명령어가 어느 python 환경에 설치 되어있는지 알수 있었다.

그리고 당연히 import 도 안된다.

$ python

Python 3.6.10 (default, Mar 31 2021, 11:22:54)
[GCC Apple LLVM 12.0.0 (clang-1200.0.32.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import jupyter
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'jupyter'

두번째 테스트 환경 - python 이 어느정도 활용된 dirty 상태

  1. pyenv global 3.6.10 로 위에서 clean install 된 3.6.10 이 global로 설정됨
  2. pyenv shell 3.6.11 기존에 마구마구 활용했던 3.6.11 을 shell 에서 사용하기
$ python --version
Python 3.6.11

$ which python
/Users/joshua/.pyenv/shims/python

$ pip --version
pip 18.1 from /Users/joshua/.pyenv/versions/3.6.11/Python.framework/Versions/3.6/lib/python3.6/site-packages/pip (python 3.6)

$ which pip
/Users/joshua/.pyenv/shims/pip

$ pyenv which pip
/Users/joshua/.pyenv/versions/3.6.11/bin/pip

global 이 뭐가 되었든, 현재 지정된 python 3.6.11 기준으로 동작한다.

여기 3.6.11 은 내가 기존에 쓰던거라 이것 저것 깔려있는데, 그중에 jupyter 를 한번 테스트에 써봤다.

$ jupyter
usage: jupyter [-h] [--version] [--config-dir] [--data-dir] [--runtime-dir]
[--paths] [--json]
[subcommand]
jupyter: error: one of the arguments --version subcommand --config-dir --data-dir --runtime-dir --paths is required

$ which jupyter
/Users/joshua/.pyenv/shims/jupyter

$ pyenv which jupyter
/Users/joshua/.pyenv/versions/3.6.11/bin/jupyter

$ python
Python 3.6.11 (default, Jul 24 2020, 11:11:24)
[GCC 4.2.1 Compatible Apple LLVM 11.0.3 (clang-1103.0.32.62)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import jupyter
>>>

아무 문제 없는게 당연하지. 원래 써 왔으니까

세번째 테스트 환경 - clean virtualenv

  1. pyenv virtualenv 3.6.11 project3 으로 위에 dirty 한 상태의 python 버전으로 project3 virtualenv 생성
  2. pyenv shell project3 지정
$ python --version
Python 3.6.11

$ which python
/Users/joshua/.pyenv/shims/python

$ pyenv which python
/Users/joshua/.pyenv/versions/project3/bin/python

$ pip --version
pip 18.1 from /Users/joshua/.pyenv/versions/3.6.11/envs/project3/lib/python3.6/site-packages/pip (python 3.6)

$ which pip
/Users/joshua/.pyenv/shims/pip

$ pyenv which pip
/Users/joshua/.pyenv/versions/project3/bin/pip

기본 커맨드는 위와 같이 잘 동작했다.
python, pip 등의 커맨드는 기존과 마찬가지로 직접적으로는 shims 를 바라보게 되어있다.
하지만 실제 커맨드의 위치(pyenv which 로 확인)는 project3 으로 생성한 virtualenv 에서 사용하게 된다.
pip 는 기본으로 python 버전을 설치하든, virtualenv 를 설치하든 기본으로 따라온다.

기본으로 따라오지 않는 jupyter 갖고 또 실험해 보자

$ jupyter
pyenv: jupyter: command not found

The `jupyter' command exists in these Python versions:
3.6.11
3.6.11/envs/project1
3.6.11/envs/project2
project1
project2

Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.

$ pyenv whence jupyter
3.6.11
3.6.11/envs/project1
3.6.11/envs/project2
project1
project2

위와 같이 virtualenv 안에서 밖의 python 의 jupyter 에는 접근이 안된다.
import 역시 안되는것으로 보아, virtualenv 는 기본 버전에 뭐가 깔려있든 완전 격리 되었다.

마지막 테스트 환경 - virtualenv 에 패키지 설치

  1. project3 에다가 pip install jupyter 로 패키지를 설치

뭐 당연히 설치 했으니 잘 동작하겠지만, 확인차..

$ which jupyter
/Users/joshua/.pyenv/shims/jupyter

$ pyenv which jupyter
/Users/joshua/.pyenv/versions/project3/bin/jupyter

결론

그렇다면, 각 python 과 virtualenv 마다 필요한게 있으면 각각 설치해줘야 한다는 결론이다.
pip 는 그냥 따라오는 기본이라 설치해주지 않아도 되는 거구요.
poetry 의 경우에는 버전마다 설치해줘야 되는게 맞고, poetry 자체에 virtualenv 관리 기능이 있어서 virtualenv 는 따로 생성하지 않고 poetry 한테 맡기면 되지 않나~ 하는 생각이다.

혹시 그렇다면 pyenv 의 관리 외에 설치된 python 에 설치된 것은, 현재 pyenv 로 어떤 version 을 선택하든지, 접근이 가능하지 않을까??
(예를 들어 mac 기본 python 이라든가, brew 로 설치한 python 이라든가..)

이거는 뭐 현재 shell 의 PATH 환경변수를 확인해 보는것으로 알 수 있다.
원래 python 에서 실행가능 binary 들은 각 환경의 bin 디렉토리 안에 저장 된다. (pyenv which 명령어로 실제 위치 확인)
그리고 env | grep PATH 를 해보면, 현재 PATH 환경 변수를 볼수 있는데, 맨앞에 pyenv 관련 shims 디렉토리들이 지정되어있는것을 확인 할 수 있다.
따라서 pyenv 관리 하에 있는 python 버전들은, 모두 이 shims 를 통해서 사용이 되며, pyenv 관리 밖에 있는 python 버전들은 따로 PATH 환경 변수에 지정하든지 해서 사용하면 될듯 하다.
하지만.. 더이상의 복잡함은 나의 두뇌에 허용하지 않겠다