zaki work log

作業ログやら生活ログやらなんやら

pyenvを使ったPython実行/開発環境の構築

普段はいつもvenvを使って環境構築するんだけど、OSアップグレードしてPythonバージョンが更新されると、venv作成時のPythonバージョンと差異が発生して環境がおかしくなってしまうので、Python自体をインストールするpyenvを試してみた。

github.com

venvが OS標準の 使用中のPythonインタプリタを流用して(pipでインストールしたりする)外部パッケージ等をシステムから分離するのに対して、pyenvはPythonそのものをユーザーディレクトリ以下へインストールするため、ディストリビューションのパッケージで用意されていないバージョンのPythonも簡単に利用できる。また、OSアップグレードに伴うPythonバージョンアップの影響を受けない。その反面セキュリティfixなどはdnfaptなどを使ったディストリビューションの管理外なので自分でメンテする必要があり、ストレージ容量もその分必要。

(追記)全体的に「venv or pyenv」って感じで書いてしまったけど、併用するのがセオリーっぽい。最後にまとめを追記。

環境

zaki@ubuntu:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.6 LTS"
zaki@ubuntu:~$ python3 --version
Python 3.8.10
zaki@ubuntu:~$ which python3
/usr/bin/python3

22.04でも同様

pyenvセットアップ

pyenvのインストール

Linuxの場合は使用するユーザーでスクリプト実行してインストールする。

curl https://pyenv.run | bash

.bashrc更新と再読み込み

cat <<'__EOL__' >> $HOME/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
__EOL__
. $HOME/.bashrc

これでpyenvを実行できる。

zaki@ubuntu:~$ pyenv --version
pyenv 2.3.17

依存パッケージのインストール

pyenvでPythonをインストールする際に必要なパッケージを入れる。
Ubuntuであれば以下の通り。

sudo apt update; sudo apt install build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

Fedoraなどその他の環境は公式のWiki参照。

github.com

pyenvでPythonインストール

pyenvの準備ができたので、pyenvを使ってPythonをインストールする。

インストールできるPythonのリスト

古いのを含めて結構ある。ActivePythonやAnacondaなども選択可能。以下は一例。

zaki@ubuntu:~$ pyenv install -l
Available versions:
  2.1.3
  2.2.3
  2.3.7
  2.4.0
  
  [...]

  2.7.17
  2.7.18
  3.0.1
  3.1.0

  [...]

  3.11.3
  3.12.0a7
  3.12-dev

  [...]

Pythonインストール

リストで確認できた、例えば3.11.3をインストールするには以下を実行する。

pyenv install 3.11.3

実行ログにどこへインストールされるか確認できる。(覚えておく必要は特にない)

zaki@ubuntu:~$ pyenv install 3.11.3
Downloading Python-3.11.3.tar.xz...
-> https://www.python.org/ftp/python/3.11.3/Python-3.11.3.tar.xz
Installing Python-3.11.3...
Installed Python-3.11.3 to /home/zaki/.pyenv/versions/3.11.3
zaki@ubuntu:~$ 

インタプリタの切り替え

OS標準のPythonやpyenvでインストールしたPythonなど複数のインタプリタからどれをpythonとして使用するかの設定を行う。

zaki@ubuntu:~$ pyenv global 3.11.3
zaki@ubuntu:~$ python --version
Python 3.11.3
zaki@ubuntu:~$ which python
/home/zaki/.pyenv/shims/python

切替可能なバージョン一覧

zaki@ubuntu:~$ pyenv versions
  system
* 3.11.3 (set by /home/zaki/.pyenv/version)

venvとの容量比較

作ったばかりの状態のvenvと、pyenvで3.11.3をインストールしたばかりのディレクトリの状態。

zaki@ubuntu:~$ du -sh .pyenv/ venv/
309M    .pyenv/
19M     venv/

(おまけ) OSアップグレードした場合

ここまでの検証環境がUbuntu 22.04でなく20.04を使ったのはこれをまとめるため。笑
前述の環境をdo-release-upgradeで22.04へアップグレードを行い、venvで作った環境とpyenvで作った環境を比較するとこの通り。(それぞれ事前にpip install ansibleでAnsibleをインストールしている)

ubuntu@ubuntu:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS"

pyenvで入れたPython 3.11.3の場合

zaki@ubuntu:~$ python --version
Python 3.11.3
zaki@ubuntu:~$ which ansible
/home/zaki/.pyenv/shims/ansible
zaki@ubuntu:~$ ansible --version
ERROR: libffi.so.7: cannot open shared object file: No such file or directory

おや?OSアップグレードで依存ライブラリが無くなったとかかな?
sudo apt-get install libffi7でインストールして再実行。

zaki@ubuntu:~$ ansible --version
ansible [core 2.14.5]
  config file = None
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/.pyenv/versions/3.11.3/lib/python3.11/site-packages/ansible
  ansible collection location = /home/zaki/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/zaki/.pyenv/versions/3.11.3/bin/ansible
  python version = 3.11.3 (main, Apr 29 2023, 08:25:24) [GCC 9.4.0] (/home/zaki/.pyenv/versions/3.11.3/bin/python3.11)
  jinja version = 3.1.2
  libyaml = True

venvの環境の場合

venv作成時はPython 3.8.10だったが、OSアップグレードでPython 3.10.6に更新されている、という状況。

(python38) zaki@ubuntu:~$ python --version
Python 3.10.6
(python38) zaki@ubuntu:~$ which ansible 
/home/zaki/venv/python38/bin/ansible
(python38) zaki@ubuntu:~$ ansible --version
Traceback (most recent call last):
  File "/home/zaki/venv/python38/bin/ansible", line 5, in <module>
    from ansible.cli.adhoc import main
ModuleNotFoundError: No module named 'ansible'

この状態になるとpip freezeも同様のエラーで使えないので再構築のやり方がわからない。。


(追記) まとめ

venvの場合でもrequirements.txtをメンテしていればOSアップグレードしても環境の作り直しは簡単なので、細かいPythonバージョンまで要件がなければ使いやすい方を選択すれば良いかな?
Pythonバージョンに指定があるならpyenvを使って環境構築すると良さそう。

(追記) 「OS標準のPythonでvenv作るとアップグレード時に環境が壊れる」がスタート地点だったので「pyenv便利じゃん?」という感じでしたが、記事アップしたらコメント複数いただいて、環境構築としては併用して「pyenvでインストールしたPythonでvenv作る」のがバージョン含めた環境の分離・切換えがやりやすくて良さそうです。