俺流!PEP668とうまくやっていく方法
PEP668は突然やってきた
pythonのスクリプト書いてるときに、OSに入っていないpythonのモジュールが必要になったらどうしてますか?
いままで、自分は細かいこと考えず、すぐ使えればいいやと割り切って
$ pip3 install --user ほしいモジュール名
って、いつもやってました(笑)。まあ、見るからに、野蛮ですね。
ところが、先日久しぶりにDebian sidにはない無いモジュール(pycoingecko)が必要になって、いつものようにpip3を実行しました。そしたら突然のエラーですよ!
$ pip3 install --user pycoingecko
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.
See /usr/share/doc/python3.11/README.venv for more information.
note: If you believe this is a mistake, please contact your Python installation
or OS distribution provider. You can override this, at the risk of breaking your
Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
なんじゃこりゃー! メッセージの通り、PEP668がヒントとか言われてます。
早速、pipのupstreamのNEWS.rstを確認すると、pip ver 23.0で搭載された機能のようです。
ここでは、自分流のやり方でPEP668と付き合う方法を書いてみます。
対策その1:余裕の無い人向け
納期に余裕がないから一番早い方法をタノム!という叫びが聞こえた気がします。そういう人には--break-system-packages
オプション一発で今までどおりのインストールができます。ここではpycoingeckoを導入してみます。
$ pip3 install --break-system-packages --user pycoingecko
Collecting pycoingecko
Using cached pycoingecko-3.1.0-py3-none-any.whl (8.8 kB)
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from
pycoingecko) (2.28.1)
Installing collected packages: pycoingecko
Successfully installed pycoingecko-3.1.0
$ pip3 list --user
Package Version
----------- -------
pycoingecko 3.1.0
もちろんアンインストールも同じオプションをつけるだけでいつも通りです!
$ pip3 uninstall --break-system-packages pycoingecko
Found existing installation: pycoingecko 3.1.0
Uninstalling pycoingecko-3.1.0:
Would remove:
/home/yours/.local/lib/python3.11/site-packages/pycoingecko-3.1.0.dist-info
/*
/home/yours/.local/lib/python3.11/site-packages/pycoingecko/*
Proceed (Y/n)? Y
Successfully uninstalled pycoingecko-3.1.0
$ pip3 list --user
...何も表示されない...
--break-system-packages
オプションがあって、良かったですね!
対策その2:メッセージの内容に従う人向け
優雅に美しくPEPを尊重という方は、そもそもpip3 install --user
なんて野蛮なことは普段からしてないはずなので大丈夫なはずです。しかしながら、「悔い改めて今日から私はPEP668準拠!ジーク・PEP!」という人もいるはずです。そういう人向けの対策を書きます。ここでは、pip3のメッセージに従い、OS側付属のpythonに標準搭載のvenvを利用するようにします。
手順は次のとおりです。
- 開発予定のプログラムを収めるディレクトリを作成します。
$ mkdir your-project
$ cd your-project
pip3 install --user
でインストールしてしまったモジュールの一覧をバックアップしつつ、消去します。こちらをしたくない人は本手順をスキップしてください。その場合、後でこれらのモジュールが思わぬところで利用されてしまい、不具合になるかもしれません。
# pip3 installで過去にいれてしまったモジュール一覧
$ pip3 list --user
Package Version
------------- -------
cachetools 5.3.0
distlib 0.3.6
filelock 3.11.0
platformdirs 3.2.0
pyproject_api 1.5.1
tox 4.4.11
virtualenv 20.21.0
# モジュール一覧をrequirement.txtに残しつつ消す
$ pip3 freeze --user | tee requirements.txt | \
xargs pip3 uninstall -y --break-system-packages
Found existing installation: cachetools 5.3.0
Uninstalling cachetools-5.3.0:
Successfully uninstalled cachetools-5.3.0
Found existing installation: distlib 0.3.6
...中略...
Found existing installation: tox 4.4.11
Uninstalling tox-4.4.11:
Successfully uninstalled tox-4.4.11
Found existing installation: virtualenv 20.21.0
Uninstalling virtualenv-20.21.0:
Successfully uninstalled virtualenv-20.21.0
$ pip3 list --user
...跡形もなく消えます...
- venvをカレントディレクトリの.venvディレクトリに導入して起動します。
なお、
ls .venv/bin
してpip3やpython3が入っていることを確かめてください。
$ python3 -m venv --system-site-packages --clear --prompt 'your-project' \
--upgrade-deps $(pwd)/.venv
$ ls .venv/bin/
Activate.ps1 activate.csh pip pip3.11 python3
activate activate.fish pip3 python python3.11
$ source .venv/bin/activate
(your-project)$ command -v python3
/home/yours/your-project/.venv/bin/python3 <--- venv付属のpython3になっている
(your-project)$ command -v pip3
/home/yours/your-project/.venv/bin/pip3 <--- venv付属のpip3になっている
- 作成したrequirements.txtに基づくモジュールをインストールします。
(your-project)$ pip3 install -r requirements.txt
Collecting cachetools==5.3.0
Using cached cachetools-5.3.0-py3-none-any.whl (9.3 kB)
Collecting distlib==0.3.6
Using cached distlib-0.3.6-py2.py3-none-any.whl (468 kB)
...中略...
Requirement already satisfied: colorama>=0.4.6 in /usr/lib/python3/dist-packages
(from tox==4.4.11->-r ./requirements.txt (line 6)) (0.4.6)
Requirement already satisfied: pluggy>=1 in /usr/lib/python3/dist-packages (from
tox==4.4.11->-r ./requirements.txt (line 6)) (1.0.0+repack)
Installing collected packages: distlib, pyproject_api, platformdirs, filelock, c
achetools, virtualenv, tox
Attempting uninstall: platformdirs
Found existing installation: platformdirs 2.6.0
Not uninstalling platformdirs at /usr/lib/python3/dist-packages, outside env
ironment /home/yours/prog/bitbank/.venv
Can't uninstall 'platformdirs'. No files were found to uninstall.
Successfully installed cachetools-5.3.0 distlib-0.3.6 filelock-3.11.0 platformdi
rs-3.2.0 pyproject_api-1.5.1 tox-4.4.11 virtualenv-20.21.0
# 無事インストールできたかを確認
(your-project)$ pip3 list | fgrep -f <(cut -f 1 -d = ./requirements.txt)
cachetools 5.3.0
distlib 0.3.6
filelock 3.11.0
platformdirs 3.2.0
pyproject_api 1.5.1
tox 4.4.11
virtualenv 20.21.0
- 準備できたので、いったんvenv環境から抜けます。
(your-project)$ deactivate
$ <--- venv環境から抜け出すとプロンプトが元通り
以上で、
- your-projectディレクトリ内で
source .venv/bin/activate
をする度に、今までpip3 install --user
で適当に導入していたモジュールが、そのまま使える deactivate
と唱えると何も導入されていない状態になる
が実現できました!
また、venv環境で作成したスクリプトの冒頭は、#!/usr/bin/env python3
と記載しておけば、activateすればvenvに導入されたpython3のツリーが利用されて実行されますので便利ですね!試しにやってみます。
$ cd your-project
$ source .venv/bin/activate
(your-project)$ cat > where_is_python.py <<__HERE
> #!/usr/bin/env python3
> import sys
> print(sys.executable)
> sys.exit(0)
> __HERE
(your-project)$ chmod 755 where_is_python.py
(your-project)$ ./where_is_python.py
/home/yours/your-project/.venv/bin/python3 <--- venvのpython3が利用されている
(your-project)$ deactivate <--- venvから抜けると、
$ ./where_is_python.py
/usr/bin/python3 <--- OS側のpython3が利用される
見ての通り、venvをactivateしていればvenv側のpython3が、venvをdeactivateしていればOS側のpython3が利用されます。
また、venvに導入したモジュールのドキュメントを読みたいときは、activateした状態で、
(your-project)$ python3 -mpydoc venvでpip3 installしたモジュール名
例:(your-project)$ python3 -mpydoc tox
で読めます。
venv使いに改心したのに、いちいちsource .venv/bin/activate
するのが面倒!という人向けには、direnvがあるようです。自分は未評価ですが、Debianパッケージにもなっている(apt install direnv
で入る)ので、dirvenvのwikiあたりでも読んで導入すると便利かもしれません。ここらあたりは、様々な流派が存在するようですので、好みの方法でactivateしてみてください。
対策その3: その他python3仮想環境の人向け
様々な理由によりvenvではなく、他のpythonの仮想環境を使っている人はどうすればいいのでしょう?例えばpyenvとか、~envとか…数々のpythonの仮想環境が存在します。
今回、pip3に施されたPEP668対応の仕組みは、以下の条件が揃うと発動します。
発動条件1:
pip3 install
の操作がOSに入っているpython3環境に影響を与える。具体的にはpip3 install
のオプションにて
--dry-run
と--report
を同時に指定していない、かつ、--root
、--target
、--prefix
、--break-system-packages
のいずれも指定していない。
発動条件2:
現在のpython3は、pythonの仮想環境の元で起動していない。具体的には、import sys
にて取得できるsys.prefix
とsys.base_prefix
の値が同じであり、sys.real_prefix
が定義されていない。
発動条件3:
import sysconfig
して取れるsysconfig.get_path("stdlib")
のパスにEXTERNALLY-MANAGED
ファイルがある。
というわけで、上述いずれかの条件を満たさないpythonの仮想環境の元であれば、PEP668は発動しません。ご自分のお使いのpythonの仮想環境で、pip3がPEP668のエラーを起こすようであれば、上述いずれかの条件を満たしていないか?を確認してみてください。
以下に簡単なテスト方法を記載しておきます。PEP668 Affected!
と出たらpip3 install
はPEP668のエラーを発動してしまうpythonの仮想環境ということになります。各pythonの仮想環境で実行するとpip3がそのまま使える、あるいは、使えないがわかると思います。
$ python3 -c 'import sys;import sysconfig;import os;print("PEP668 affected!") \
if sys.prefix == sys.base_prefix and not hasattr(sys,"real_prefix") and \
os.path.isfile(os.path.join(sysconfig.get_path("stdlib"), \
"EXTERNALLY-MANAGED")) else print("PEP668 not affected!")'
# PEP668のエラーがpip3 installで出る環境
PEP668 Affected!
# PEP668のエラーがpip3 installで出ない環境
PEP668 not Affected!
PEP668とはなんぞや
先に対策を書いて気持ちがすっきりしたので、ここからはPEP668について書きます。
PEP668に定義があります。内容としては、
OSが用意するパッケージのコマンド(dpkg/apt/dnf等)の管理外で、python用のモジュール管理コマンド(例:pip3等)を不用意に使うとOS側のpythonの環境に悪影響を及ぼしてしまうのを防ぎたいというものです。提案されているのは、OS側のpython3のディレクトリにEXTERNALLY-MANAGED
ファイルをおいたら、そこはpython用のモジュール管理コマンドの影響から保護してね!という内容です。
OSに詳しい人なら普段から疑問に思ってた!という件ですし、PEP668はこちらの問題に真正面から取り組むものではあるのですが、そうじゃない人に自分もうまく説明できる自信がありません。ここは何でも知ってるChatGPTに問い合わせてみました。
おお、for 5 years old boy
は凄い威力です!おもちゃ(pythonモジュールの事)をわけましょうね!というとてもわかり易い内容ですね。PEP668を5歳児に説明ができると言い張る時点で、ChatGPTは自分の中ではシンギュラリティ超えしてます。
pipxは?ねえ、pipxは?
冒頭のpip3の表示するメッセージをちゃんと読むと、pythonアプリケーション導入するならpipxを推すという内容が書かれていると思います。
pipxは、
pipx install hoge
とやると、$(HOME)/.local/pipx/venvs/hoge
という名前のvenv環境が生成され、hogeアプリケーションに必要なモジュールがインストールされます。hogeアプリケーション本体はこちらのvenvの環境で稼働するように調整されて$(HOME)/.local/bin
に入ります。パッケージの名前がvenvの環境名になるので、pipx install bar
とやると、既存のhogeアプリケーションとはまったく違うvenv環境が新たに生成されてインストールされます。このため、すでにpipxで導入したhogeプリケーションと、barアプリケーションはvenv環境がそもそも違うので競合しません。- もし、追加で別のモジュールfooをhogeアプリケーションに導入したい場合は、
pipx inject hoge foo
とやると、hogeのvenv環境にfooモジュールを追加できます。
という、python製のアプリケーション導入に向いたpythonのパッケージインストーラとなります。いちいちactivateしなくてもアプリケーション専用のvenv環境の元で実行されるので確かに便利です。
なにかpython製のアプリケーションをpip3 install --user
でいつもインストールしていた人は、代わりにpipxで導入するようにすると便利かもしれません。
ここでは、例としてbpytopアプリケーションを導入してみます。
$ sudo apt install pipx
...pipxが導入される...
$ pipx install bpytop
...中略...
$ tree -L 4 ~/.local ← .local/bin/にコマンドが導入され、.local/pipx以下にvenv環境がbpytopの名前で展開される
.local/
├── bin
│ └── bpytop -> /home/yours/.local/pipx/venvs/bpytop/bin/bpytop
└── pipx
├── logs
│ └── cmd_2023-04-11_07.18.32.log
├── shared
│ ├── bin
│ │ ├── Activate.ps1
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── pip
│ │ ├── pip3
│ │ ├── pip3.11
│ │ ├── python -> python3
│ │ ├── python3 -> /usr/bin/python3
│ │ ├── python3.11 -> python3
│ │ └── wheel
│ ├── include
│ │ └── python3.11
│ ├── lib
│ │ └── python3.11
│ ├── lib64 -> lib
│ └── pyvenv.cfg
└── venvs
└── bpytop <---ここがアプリケーション名で新たに作られる。
├── bin
├── include
├── lib
├── lib64 -> lib
├── pipx_metadata.json
└── pyvenv.cfg
17 directories, 16 files
$ head ~/.local/bin/bpytop ← ~/.local/pipx/venvs/bpytop以下の
← venvをうまく利用するようにSHELL BANG(行頭1行目)が修正済
#!/home/yours/.local/pipx/venvs/bpytop/bin/python
# -*- coding: utf-8 -*-
import re
import sys
$ ~/.local/bin/bpytop
...bpytopが動く...
終わりに
突然やってきたPEP668と仲良くする方法を掲載しました。
PEP668は、OS側のpythonも尊重しつつ、開発もうまくやってほしい!ということだと思いますので、今後は面倒がらずにpythonの仮想環境と仲良くしていくのも良いと思います。