環境変数を間違えてPoetryがうまく動かなくなった話

はじめに

PoetryはPythonの強力なパッケージ依存関係管理ツールです.

ところで,PoetryはPythonの外部パッケージを扱うツールであって,Python本体そのものを扱うツールではないため,当然ながらPythonの本体そのものが複数存在する場合に,そのうちのどれを使用するかを明示的にコントロールすることはできません.

このため,Pythonのパスの通し方がまずいとPoetryが予想外の挙動をしてしまうことがあります.
私はこれが原因ですっかり泥沼にハマってしまったため,備忘録として残しておきます.

動作環境

~ $ cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)
~ $ echo $SHELL
/bin/zsh

何が起こったか

普段私はもともと研究用サーバーの共用部分 (/usr/local/bin) に入っている Python 3.6.4 を使っています.

このため,python hogehoge というコマンドでPythonを実行する際には当然これが使用されます:

~ $ which python
/usr/local/bin
~ $ python --version
Python 3.6.4

これは /usr/local/bin にパスが通っているからこそ実現できることです:

~ $ echo $PATH
/home/myname/.linuxbrew/bin:/usr/local/bin:...
                            ^^^^^^^^^^^^^^

それでは,myprojectというプロジェクト用ディレクトリを作り,プロジェクトを初期化しましょう:

~ $ mkdir myproject && cd $_
~/myproject $ poetry init

This command will guide you through creating your pyproject.toml config.

Package name [myproject]:  
Version [0.1.0]:  
Description []:  
Author [username <user@example.com>, n to skip]:  
License []:  
Compatible Python versions [^3.6]:  

Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no
Generated file

[tool.poetry]
name = "myproject"
version = "0.1.0"
description = ""
authors = ["username <user@example.com>"]

[tool.poetry.dependencies]
python = "^3.6"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"


Do you confirm generation? (yes/no) [yes] yes

次に仮想環境を作成し,作成した仮想環境に入ります:

~/myproject $ poetry shell
Creating virtualenv myproject in /home/username/myproject/.venv
Spawning shell within /home/username/myproject/.venv
(.venv) ~/myproject . /home/username/myproject/.venv/bin/activate

この状態でPythonインタラクティブセッションを立ち上げてみましょう:

Python 3.9.5 (default, May 13 2021, 20:57:30) 
[GCC 5.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

急に Python 3.9.5 になった!?!?

なぜこうなったのか

実は自分の $HOME ディレクトリ以下にいつインストールしたのかも覚えていないようなPythonがもう1つあり,そちらが代わりに呼ばれてしまっていたのです.

つまり以下の2つのPythonが共存していたことになります.

  • ①もともと共用サーバーに入っていたPython 3.6.4
    • 場所: /usr/local/bin
  • ②いつの間にかHomebrewで自分の $HOME 以下にインストールしていたPython 3.9.4
    • 場所: ~/.linuxbrew/bin

ここでもう一度環境変数 $PATH をみてみましょう.

~ $ echo $PATH
/home/myname/.linuxbrew/bin:/usr/local/bin:...
                            ^^^^^^^^^^^^^^

~/.linuxbrew/bin と ①/usr/local/bin の両方にパスが通っていますが,先に書かれているのは②のほうです.

どうやらPoetryでPython仮想環境を作成する場合,Pythonの本体は パスが最初に通る場所に格納されているものが選ばれる ようであり,このため②にインストールされている Python 3.9.5 が使用されることになったようです.

したがって,仮想環境内でも①のPythonを使いたければ,$PATH の順序を入れ替えて

export PATH=/usr/local/bin:$PATH

とでもすべきということになります.

では,なぜ which python では①のパスが返されたのでしょうか?
答えはシンボリックリンクの有無の違いにありました.

①からは pythonシンボリックリンクが張られている一方で...

~ $ ls -l /usr/local/bin | grep python
lrwxrwxrwx. 1 root root ... python -> /usr/local/python/bin/python3

②からは pythonシンボリックリンクが張られていません.一方 python3, python3.9 などのシンボリックリンクは張られています.

~ $ ls -l .linuxbrew/bin | grep python
lrwxrwxrwx. 1 myname myname ... python3 -> ../Cellar/python@3.9/3.9.5/bin/python3
lrwxrwxrwx. 1 myname myname ... python3-config -> ../Cellar/python@3.9/3.9.5/bin/python3-config
lrwxrwxrwx. 1 myname myname ... python3.9 -> ../Cellar/python@3.9/3.9.5/bin/python3.9
lrwxrwxrwx. 1 myname myname ... python3.9-config -> ../Cellar/python@3.9/3.9.5/bin/python3.9-config

おそらく仮想環境構築時にはPython本体は python コマンドだけでなく(?),python3コマンドなどでも探索されるのだと思われます.

そのような探索の仕方をすれば,当然Python本体がはじめに見つかる場所は②です.

しかし,python コマンドのパスだけは①に通っています.そして,普段使っているコマンドも python であったために,python3 のパスが②に通っていることに気付けなかったというわけです.